commit
a801f03cf2
2
pom.xml
2
pom.xml
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<groupId>fr.xephi</groupId>
|
<groupId>fr.xephi</groupId>
|
||||||
<artifactId>authme</artifactId>
|
<artifactId>authme</artifactId>
|
||||||
<version>5.3.3-SNAPSHOT</version>
|
<version>5.4-SNAPSHOT</version>
|
||||||
|
|
||||||
<name>AuthMeReloaded</name>
|
<name>AuthMeReloaded</name>
|
||||||
<description>The first authentication plugin for the Bukkit API!</description>
|
<description>The first authentication plugin for the Bukkit API!</description>
|
||||||
|
|||||||
@ -5,17 +5,15 @@ import com.zaxxer.hikari.HikariDataSource;
|
|||||||
import com.zaxxer.hikari.pool.HikariPool.PoolInitializationException;
|
import com.zaxxer.hikari.pool.HikariPool.PoolInitializationException;
|
||||||
import fr.xephi.authme.ConsoleLogger;
|
import fr.xephi.authme.ConsoleLogger;
|
||||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||||
import fr.xephi.authme.security.HashAlgorithm;
|
import fr.xephi.authme.datasource.mysqlextensions.MySqlExtension;
|
||||||
|
import fr.xephi.authme.datasource.mysqlextensions.MySqlExtensionsFactory;
|
||||||
import fr.xephi.authme.security.crypts.HashedPassword;
|
import fr.xephi.authme.security.crypts.HashedPassword;
|
||||||
import fr.xephi.authme.security.crypts.XfBCrypt;
|
|
||||||
import fr.xephi.authme.settings.Settings;
|
import fr.xephi.authme.settings.Settings;
|
||||||
import fr.xephi.authme.settings.properties.DatabaseSettings;
|
import fr.xephi.authme.settings.properties.DatabaseSettings;
|
||||||
import fr.xephi.authme.settings.properties.HooksSettings;
|
import fr.xephi.authme.settings.properties.HooksSettings;
|
||||||
import fr.xephi.authme.settings.properties.SecuritySettings;
|
|
||||||
import fr.xephi.authme.util.StringUtils;
|
import fr.xephi.authme.util.StringUtils;
|
||||||
import fr.xephi.authme.util.Utils;
|
import fr.xephi.authme.util.Utils;
|
||||||
|
|
||||||
import java.sql.Blob;
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.DatabaseMetaData;
|
import java.sql.DatabaseMetaData;
|
||||||
import java.sql.PreparedStatement;
|
import java.sql.PreparedStatement;
|
||||||
@ -44,19 +42,11 @@ public class MySQL implements DataSource {
|
|||||||
private int maxLifetime;
|
private int maxLifetime;
|
||||||
private List<String> columnOthers;
|
private List<String> columnOthers;
|
||||||
private Columns col;
|
private Columns col;
|
||||||
private HashAlgorithm hashAlgorithm;
|
private MySqlExtension sqlExtension;
|
||||||
private HikariDataSource ds;
|
private HikariDataSource ds;
|
||||||
|
|
||||||
private String phpBbPrefix;
|
public MySQL(Settings settings, MySqlExtensionsFactory extensionsFactory) throws SQLException {
|
||||||
private String ipbPrefix;
|
setParameters(settings, extensionsFactory);
|
||||||
private String xfPrefix;
|
|
||||||
private String wordpressPrefix;
|
|
||||||
private int phpBbGroup;
|
|
||||||
private int ipbGroup;
|
|
||||||
private int xfGroup;
|
|
||||||
|
|
||||||
public MySQL(Settings settings) throws ClassNotFoundException, SQLException {
|
|
||||||
setParameters(settings);
|
|
||||||
|
|
||||||
// Set the connection arguments (and check if connection is ok)
|
// Set the connection arguments (and check if connection is ok)
|
||||||
try {
|
try {
|
||||||
@ -86,17 +76,18 @@ public class MySQL implements DataSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
MySQL(Settings settings, HikariDataSource hikariDataSource) {
|
MySQL(Settings settings, HikariDataSource hikariDataSource, MySqlExtensionsFactory extensionsFactory) {
|
||||||
ds = hikariDataSource;
|
ds = hikariDataSource;
|
||||||
setParameters(settings);
|
setParameters(settings, extensionsFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves various settings.
|
* Retrieves various settings.
|
||||||
*
|
*
|
||||||
* @param settings the settings to read properties from
|
* @param settings the settings to read properties from
|
||||||
|
* @param extensionsFactory factory to create the MySQL extension
|
||||||
*/
|
*/
|
||||||
private void setParameters(Settings settings) {
|
private void setParameters(Settings settings, MySqlExtensionsFactory extensionsFactory) {
|
||||||
this.host = settings.getProperty(DatabaseSettings.MYSQL_HOST);
|
this.host = settings.getProperty(DatabaseSettings.MYSQL_HOST);
|
||||||
this.port = settings.getProperty(DatabaseSettings.MYSQL_PORT);
|
this.port = settings.getProperty(DatabaseSettings.MYSQL_PORT);
|
||||||
this.username = settings.getProperty(DatabaseSettings.MYSQL_USERNAME);
|
this.username = settings.getProperty(DatabaseSettings.MYSQL_USERNAME);
|
||||||
@ -105,14 +96,7 @@ public class MySQL implements DataSource {
|
|||||||
this.tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
|
this.tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
|
||||||
this.columnOthers = settings.getProperty(HooksSettings.MYSQL_OTHER_USERNAME_COLS);
|
this.columnOthers = settings.getProperty(HooksSettings.MYSQL_OTHER_USERNAME_COLS);
|
||||||
this.col = new Columns(settings);
|
this.col = new Columns(settings);
|
||||||
this.hashAlgorithm = settings.getProperty(SecuritySettings.PASSWORD_HASH);
|
sqlExtension = extensionsFactory.buildExtension(col);
|
||||||
this.phpBbPrefix = settings.getProperty(HooksSettings.PHPBB_TABLE_PREFIX);
|
|
||||||
this.phpBbGroup = settings.getProperty(HooksSettings.PHPBB_ACTIVATED_GROUP_ID);
|
|
||||||
this.ipbPrefix = settings.getProperty(HooksSettings.IPB_TABLE_PREFIX);
|
|
||||||
this.ipbGroup = settings.getProperty(HooksSettings.IPB_ACTIVATED_GROUP_ID);
|
|
||||||
this.xfPrefix = settings.getProperty(HooksSettings.XF_TABLE_PREFIX);
|
|
||||||
this.xfGroup = settings.getProperty(HooksSettings.XF_ACTIVATED_GROUP_ID);
|
|
||||||
this.wordpressPrefix = settings.getProperty(HooksSettings.WORDPRESS_TABLE_PREFIX);
|
|
||||||
this.poolSize = settings.getProperty(DatabaseSettings.MYSQL_POOL_SIZE);
|
this.poolSize = settings.getProperty(DatabaseSettings.MYSQL_POOL_SIZE);
|
||||||
if (poolSize == -1) {
|
if (poolSize == -1) {
|
||||||
poolSize = Utils.getCoreCount() * 3;
|
poolSize = Utils.getCoreCount() * 3;
|
||||||
@ -303,28 +287,14 @@ public class MySQL implements DataSource {
|
|||||||
PlayerAuth auth;
|
PlayerAuth auth;
|
||||||
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
|
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
|
||||||
pst.setString(1, user.toLowerCase());
|
pst.setString(1, user.toLowerCase());
|
||||||
int id;
|
|
||||||
try (ResultSet rs = pst.executeQuery()) {
|
try (ResultSet rs = pst.executeQuery()) {
|
||||||
if (!rs.next()) {
|
if (rs.next()) {
|
||||||
return null;
|
int id = rs.getInt(col.ID);
|
||||||
}
|
auth = buildAuthFromResultSet(rs);
|
||||||
id = rs.getInt(col.ID);
|
sqlExtension.extendAuth(auth, id, con);
|
||||||
auth = buildAuthFromResultSet(rs);
|
return auth;
|
||||||
}
|
|
||||||
if (hashAlgorithm == HashAlgorithm.XFBCRYPT) {
|
|
||||||
try (PreparedStatement pst2 = con.prepareStatement(
|
|
||||||
"SELECT data FROM " + xfPrefix + "user_authenticate WHERE " + col.ID + "=?;")) {
|
|
||||||
pst2.setInt(1, id);
|
|
||||||
try (ResultSet rs = pst2.executeQuery()) {
|
|
||||||
if (rs.next()) {
|
|
||||||
Blob blob = rs.getBlob("data");
|
|
||||||
byte[] bytes = blob.getBytes(1, (int) blob.length());
|
|
||||||
auth.setPassword(new HashedPassword(XfBCrypt.getHashFromBlob(bytes)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return auth;
|
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
logSqlException(ex);
|
logSqlException(ex);
|
||||||
}
|
}
|
||||||
@ -364,231 +334,8 @@ public class MySQL implements DataSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (hashAlgorithm == HashAlgorithm.IPB4) {
|
|
||||||
sql = "SELECT " + col.ID + " FROM " + tableName + " WHERE " + col.NAME + "=?;";
|
|
||||||
try (PreparedStatement pst = con.prepareStatement(sql)) {
|
|
||||||
pst.setString(1, auth.getNickname());
|
|
||||||
try (ResultSet rs = pst.executeQuery()) {
|
|
||||||
if (rs.next()) {
|
|
||||||
// Update player group in core_members
|
|
||||||
sql = "UPDATE " + ipbPrefix + tableName + " SET " + tableName + ".member_group_id=? WHERE " + col.NAME + "=?;";
|
|
||||||
try (PreparedStatement pst2 = con.prepareStatement(sql)) {
|
|
||||||
pst2.setInt(1, ipbGroup);
|
|
||||||
pst2.setString(2, auth.getNickname());
|
|
||||||
pst2.executeUpdate();
|
|
||||||
}
|
|
||||||
// Get current time without ms
|
|
||||||
long time = System.currentTimeMillis() / 1000;
|
|
||||||
// update joined date
|
|
||||||
sql = "UPDATE " + ipbPrefix + tableName + " SET " + tableName + ".joined=? WHERE " + col.NAME + "=?;";
|
|
||||||
try (PreparedStatement pst2 = con.prepareStatement(sql)) {
|
|
||||||
pst2.setLong(1, time);
|
|
||||||
pst2.setString(2, auth.getNickname());
|
|
||||||
pst2.executeUpdate();
|
|
||||||
}
|
|
||||||
// Update last_visit
|
|
||||||
sql = "UPDATE " + ipbPrefix + tableName + " SET " + tableName + ".last_visit=? WHERE " + col.NAME + "=?;";
|
|
||||||
try (PreparedStatement pst2 = con.prepareStatement(sql)) {
|
|
||||||
pst2.setLong(1, time);
|
|
||||||
pst2.setString(2, auth.getNickname());
|
|
||||||
pst2.executeUpdate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (hashAlgorithm == HashAlgorithm.PHPBB) {
|
|
||||||
sql = "SELECT " + col.ID + " FROM " + tableName + " WHERE " + col.NAME + "=?;";
|
|
||||||
try (PreparedStatement pst = con.prepareStatement(sql)) {
|
|
||||||
pst.setString(1, auth.getNickname());
|
|
||||||
try (ResultSet rs = pst.executeQuery()) {
|
|
||||||
if (rs.next()) {
|
|
||||||
int id = rs.getInt(col.ID);
|
|
||||||
// Insert player in phpbb_user_group
|
|
||||||
sql = "INSERT INTO " + phpBbPrefix
|
|
||||||
+ "user_group (group_id, user_id, group_leader, user_pending) VALUES (?,?,?,?);";
|
|
||||||
try (PreparedStatement pst2 = con.prepareStatement(sql)) {
|
|
||||||
pst2.setInt(1, phpBbGroup);
|
|
||||||
pst2.setInt(2, id);
|
|
||||||
pst2.setInt(3, 0);
|
|
||||||
pst2.setInt(4, 0);
|
|
||||||
pst2.executeUpdate();
|
|
||||||
}
|
|
||||||
// Update username_clean in phpbb_users
|
|
||||||
sql = "UPDATE " + tableName + " SET " + tableName
|
|
||||||
+ ".username_clean=? WHERE " + col.NAME + "=?;";
|
|
||||||
try (PreparedStatement pst2 = con.prepareStatement(sql)) {
|
|
||||||
pst2.setString(1, auth.getNickname());
|
|
||||||
pst2.setString(2, auth.getNickname());
|
|
||||||
pst2.executeUpdate();
|
|
||||||
}
|
|
||||||
// Update player group in phpbb_users
|
|
||||||
sql = "UPDATE " + tableName + " SET " + tableName
|
|
||||||
+ ".group_id=? WHERE " + col.NAME + "=?;";
|
|
||||||
try (PreparedStatement pst2 = con.prepareStatement(sql)) {
|
|
||||||
pst2.setInt(1, phpBbGroup);
|
|
||||||
pst2.setString(2, auth.getNickname());
|
|
||||||
pst2.executeUpdate();
|
|
||||||
}
|
|
||||||
// Get current time without ms
|
|
||||||
long time = System.currentTimeMillis() / 1000;
|
|
||||||
// Update user_regdate
|
|
||||||
sql = "UPDATE " + tableName + " SET " + tableName
|
|
||||||
+ ".user_regdate=? WHERE " + col.NAME + "=?;";
|
|
||||||
try (PreparedStatement pst2 = con.prepareStatement(sql)) {
|
|
||||||
pst2.setLong(1, time);
|
|
||||||
pst2.setString(2, auth.getNickname());
|
|
||||||
pst2.executeUpdate();
|
|
||||||
}
|
|
||||||
// Update user_lastvisit
|
|
||||||
sql = "UPDATE " + tableName + " SET " + tableName
|
|
||||||
+ ".user_lastvisit=? WHERE " + col.NAME + "=?;";
|
|
||||||
try (PreparedStatement pst2 = con.prepareStatement(sql)) {
|
|
||||||
pst2.setLong(1, time);
|
|
||||||
pst2.setString(2, auth.getNickname());
|
|
||||||
pst2.executeUpdate();
|
|
||||||
}
|
|
||||||
// Increment num_users
|
|
||||||
sql = "UPDATE " + phpBbPrefix
|
|
||||||
+ "config SET config_value = config_value + 1 WHERE config_name = 'num_users';";
|
|
||||||
try (PreparedStatement pst2 = con.prepareStatement(sql)) {
|
|
||||||
pst2.executeUpdate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (hashAlgorithm == HashAlgorithm.WORDPRESS) {
|
|
||||||
// NOTE: Eclipse says pst should be closed HERE, but it's a bug, we already close it above. -sgdc3
|
|
||||||
try (PreparedStatement pst = con.prepareStatement("SELECT " + col.ID + " FROM " + tableName + " WHERE " + col.NAME + "=?;")) {
|
|
||||||
pst.setString(1, auth.getNickname());
|
|
||||||
try (ResultSet rs = pst.executeQuery()) {
|
|
||||||
if (rs.next()) {
|
|
||||||
int id = rs.getInt(col.ID);
|
|
||||||
sql = "INSERT INTO " + wordpressPrefix + "usermeta (user_id, meta_key, meta_value) VALUES (?,?,?)";
|
|
||||||
try (PreparedStatement pst2 = con.prepareStatement(sql)) {
|
|
||||||
// First Name
|
|
||||||
pst2.setInt(1, id);
|
|
||||||
pst2.setString(2, "first_name");
|
|
||||||
pst2.setString(3, "");
|
|
||||||
pst2.addBatch();
|
|
||||||
// Last Name
|
|
||||||
pst2.setInt(1, id);
|
|
||||||
pst2.setString(2, "last_name");
|
|
||||||
pst2.setString(3, "");
|
|
||||||
pst2.addBatch();
|
|
||||||
// Nick Name
|
|
||||||
pst2.setInt(1, id);
|
|
||||||
pst2.setString(2, "nickname");
|
|
||||||
pst2.setString(3, auth.getNickname());
|
|
||||||
pst2.addBatch();
|
|
||||||
// Description
|
|
||||||
pst2.setInt(1, id);
|
|
||||||
pst2.setString(2, "description");
|
|
||||||
pst2.setString(3, "");
|
|
||||||
pst2.addBatch();
|
|
||||||
// Rich_Editing
|
|
||||||
pst2.setInt(1, id);
|
|
||||||
pst2.setString(2, "rich_editing");
|
|
||||||
pst2.setString(3, "true");
|
|
||||||
pst2.addBatch();
|
|
||||||
// Comments_Shortcuts
|
|
||||||
pst2.setInt(1, id);
|
|
||||||
pst2.setString(2, "comment_shortcuts");
|
|
||||||
pst2.setString(3, "false");
|
|
||||||
pst2.addBatch();
|
|
||||||
// admin_color
|
|
||||||
pst2.setInt(1, id);
|
|
||||||
pst2.setString(2, "admin_color");
|
|
||||||
pst2.setString(3, "fresh");
|
|
||||||
pst2.addBatch();
|
|
||||||
// use_ssl
|
|
||||||
pst2.setInt(1, id);
|
|
||||||
pst2.setString(2, "use_ssl");
|
|
||||||
pst2.setString(3, "0");
|
|
||||||
pst2.addBatch();
|
|
||||||
// show_admin_bar_front
|
|
||||||
pst2.setInt(1, id);
|
|
||||||
pst2.setString(2, "show_admin_bar_front");
|
|
||||||
pst2.setString(3, "true");
|
|
||||||
pst2.addBatch();
|
|
||||||
// wp_capabilities
|
|
||||||
pst2.setInt(1, id);
|
|
||||||
pst2.setString(2, wordpressPrefix + "capabilities");
|
|
||||||
pst2.setString(3, "a:1:{s:10:\"subscriber\";b:1;}");
|
|
||||||
pst2.addBatch();
|
|
||||||
// wp_user_level
|
|
||||||
pst2.setInt(1, id);
|
|
||||||
pst2.setString(2, wordpressPrefix + "user_level");
|
|
||||||
pst2.setString(3, "0");
|
|
||||||
pst2.addBatch();
|
|
||||||
// default_password_nag
|
|
||||||
pst2.setInt(1, id);
|
|
||||||
pst2.setString(2, "default_password_nag");
|
|
||||||
pst2.setString(3, "");
|
|
||||||
pst2.addBatch();
|
|
||||||
|
|
||||||
// Execute queries
|
sqlExtension.saveAuth(auth, con);
|
||||||
pst2.executeBatch();
|
|
||||||
pst2.clearBatch();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (hashAlgorithm == HashAlgorithm.XFBCRYPT) {
|
|
||||||
// NOTE: Eclipse says pst should be closed HERE, but it's a bug, we already close it above. -sgdc3
|
|
||||||
try (PreparedStatement pst = con.prepareStatement("SELECT " + col.ID + " FROM " + tableName + " WHERE " + col.NAME + "=?;")) {
|
|
||||||
pst.setString(1, auth.getNickname());
|
|
||||||
try (ResultSet rs = pst.executeQuery()) {
|
|
||||||
if (rs.next()) {
|
|
||||||
int id = rs.getInt(col.ID);
|
|
||||||
// Insert player password, salt in xf_user_authenticate
|
|
||||||
sql = "INSERT INTO " + xfPrefix + "user_authenticate (user_id, scheme_class, data) VALUES (?,?,?)";
|
|
||||||
try (PreparedStatement pst2 = con.prepareStatement(sql)) {
|
|
||||||
pst2.setInt(1, id);
|
|
||||||
pst2.setString(2, XfBCrypt.SCHEME_CLASS);
|
|
||||||
String serializedHash = XfBCrypt.serializeHash(auth.getPassword().getHash());
|
|
||||||
byte[] bytes = serializedHash.getBytes();
|
|
||||||
Blob blob = con.createBlob();
|
|
||||||
blob.setBytes(1, bytes);
|
|
||||||
pst2.setBlob(3, blob);
|
|
||||||
pst2.executeUpdate();
|
|
||||||
}
|
|
||||||
// Update player group in xf_users
|
|
||||||
sql = "UPDATE " + tableName + " SET " + tableName + ".user_group_id=? WHERE " + col.NAME + "=?;";
|
|
||||||
try (PreparedStatement pst2 = con.prepareStatement(sql)) {
|
|
||||||
pst2.setInt(1, xfGroup);
|
|
||||||
pst2.setString(2, auth.getNickname());
|
|
||||||
pst2.executeUpdate();
|
|
||||||
}
|
|
||||||
// Update player permission combination in xf_users
|
|
||||||
sql = "UPDATE " + tableName + " SET " + tableName + ".permission_combination_id=? WHERE " + col.NAME + "=?;";
|
|
||||||
try (PreparedStatement pst2 = con.prepareStatement(sql)) {
|
|
||||||
pst2.setInt(1, xfGroup);
|
|
||||||
pst2.setString(2, auth.getNickname());
|
|
||||||
pst2.executeUpdate();
|
|
||||||
}
|
|
||||||
// Insert player privacy combination in xf_user_privacy
|
|
||||||
sql = "INSERT INTO " + xfPrefix + "user_privacy (user_id, allow_view_profile, allow_post_profile, allow_send_personal_conversation, allow_view_identities, allow_receive_news_feed) VALUES (?,?,?,?,?,?)";
|
|
||||||
try (PreparedStatement pst2 = con.prepareStatement(sql)) {
|
|
||||||
pst2.setInt(1, id);
|
|
||||||
pst2.setString(2, "everyone");
|
|
||||||
pst2.setString(3, "members");
|
|
||||||
pst2.setString(4, "members");
|
|
||||||
pst2.setString(5, "everyone");
|
|
||||||
pst2.setString(6, "everyone");
|
|
||||||
pst2.executeUpdate();
|
|
||||||
}
|
|
||||||
// Insert player group relation in xf_user_group_relation
|
|
||||||
sql = "INSERT INTO " + xfPrefix + "user_group_relation (user_id, user_group_id, is_primary) VALUES (?,?,?)";
|
|
||||||
try (PreparedStatement pst2 = con.prepareStatement(sql)) {
|
|
||||||
pst2.setInt(1, id);
|
|
||||||
pst2.setInt(2, xfGroup);
|
|
||||||
pst2.setString(3, "1");
|
|
||||||
pst2.executeUpdate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
logSqlException(ex);
|
logSqlException(ex);
|
||||||
@ -624,35 +371,7 @@ public class MySQL implements DataSource {
|
|||||||
pst.executeUpdate();
|
pst.executeUpdate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (hashAlgorithm == HashAlgorithm.XFBCRYPT) {
|
sqlExtension.changePassword(user, password, con);
|
||||||
String sql = "SELECT " + col.ID + " FROM " + tableName + " WHERE " + col.NAME + "=?;";
|
|
||||||
try (PreparedStatement pst = con.prepareStatement(sql)) {
|
|
||||||
pst.setString(1, user);
|
|
||||||
try (ResultSet rs = pst.executeQuery()) {
|
|
||||||
if (rs.next()) {
|
|
||||||
int id = rs.getInt(col.ID);
|
|
||||||
// Insert password in the correct table
|
|
||||||
sql = "UPDATE " + xfPrefix + "user_authenticate SET data=? WHERE " + col.ID + "=?;";
|
|
||||||
PreparedStatement pst2 = con.prepareStatement(sql);
|
|
||||||
String serializedHash = XfBCrypt.serializeHash(password.getHash());
|
|
||||||
byte[] bytes = serializedHash.getBytes();
|
|
||||||
Blob blob = con.createBlob();
|
|
||||||
blob.setBytes(1, bytes);
|
|
||||||
pst2.setBlob(1, blob);
|
|
||||||
pst2.setInt(2, id);
|
|
||||||
pst2.executeUpdate();
|
|
||||||
pst2.close();
|
|
||||||
// ...
|
|
||||||
sql = "UPDATE " + xfPrefix + "user_authenticate SET scheme_class=? WHERE " + col.ID + "=?;";
|
|
||||||
pst2 = con.prepareStatement(sql);
|
|
||||||
pst2.setString(1, XfBCrypt.SCHEME_CLASS);
|
|
||||||
pst2.setInt(2, id);
|
|
||||||
pst2.executeUpdate();
|
|
||||||
pst2.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
logSqlException(ex);
|
logSqlException(ex);
|
||||||
@ -704,22 +423,7 @@ public class MySQL implements DataSource {
|
|||||||
user = user.toLowerCase();
|
user = user.toLowerCase();
|
||||||
String sql = "DELETE FROM " + tableName + " WHERE " + col.NAME + "=?;";
|
String sql = "DELETE FROM " + tableName + " WHERE " + col.NAME + "=?;";
|
||||||
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
|
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
|
||||||
if (hashAlgorithm == HashAlgorithm.XFBCRYPT) {
|
sqlExtension.removeAuth(user, con);
|
||||||
sql = "SELECT " + col.ID + " FROM " + tableName + " WHERE " + col.NAME + "=?;";
|
|
||||||
try (PreparedStatement xfSelect = con.prepareStatement(sql)) {
|
|
||||||
xfSelect.setString(1, user);
|
|
||||||
try (ResultSet rs = xfSelect.executeQuery()) {
|
|
||||||
if (rs.next()) {
|
|
||||||
int id = rs.getInt(col.ID);
|
|
||||||
sql = "DELETE FROM " + xfPrefix + "user_authenticate WHERE " + col.ID + "=?;";
|
|
||||||
try (PreparedStatement xfDelete = con.prepareStatement(sql)) {
|
|
||||||
xfDelete.setInt(1, id);
|
|
||||||
xfDelete.executeUpdate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pst.setString(1, user.toLowerCase());
|
pst.setString(1, user.toLowerCase());
|
||||||
pst.executeUpdate();
|
pst.executeUpdate();
|
||||||
return true;
|
return true;
|
||||||
@ -732,8 +436,8 @@ public class MySQL implements DataSource {
|
|||||||
@Override
|
@Override
|
||||||
public boolean updateQuitLoc(PlayerAuth auth) {
|
public boolean updateQuitLoc(PlayerAuth auth) {
|
||||||
String sql = "UPDATE " + tableName
|
String sql = "UPDATE " + tableName
|
||||||
+ " SET " + col.LASTLOC_X + " =?, " + col.LASTLOC_Y + "=?, " + col.LASTLOC_Z + "=?, " + col.LASTLOC_WORLD + "=?, "
|
+ " SET " + col.LASTLOC_X + " =?, " + col.LASTLOC_Y + "=?, " + col.LASTLOC_Z + "=?, "
|
||||||
+ col.LASTLOC_YAW + "=?, " + col.LASTLOC_PITCH + "=?"
|
+ col.LASTLOC_WORLD + "=?, " + col.LASTLOC_YAW + "=?, " + col.LASTLOC_PITCH + "=?"
|
||||||
+ " WHERE " + col.NAME + "=?;";
|
+ " WHERE " + col.NAME + "=?;";
|
||||||
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
|
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
|
||||||
pst.setDouble(1, auth.getQuitLocX());
|
pst.setDouble(1, auth.getQuitLocX());
|
||||||
@ -922,26 +626,12 @@ public class MySQL implements DataSource {
|
|||||||
@Override
|
@Override
|
||||||
public List<PlayerAuth> getAllAuths() {
|
public List<PlayerAuth> getAllAuths() {
|
||||||
List<PlayerAuth> auths = new ArrayList<>();
|
List<PlayerAuth> auths = new ArrayList<>();
|
||||||
try (Connection con = getConnection()) {
|
try (Connection con = getConnection(); Statement st = con.createStatement()) {
|
||||||
try (Statement st = con.createStatement()) {
|
try (ResultSet rs = st.executeQuery("SELECT * FROM " + tableName)) {
|
||||||
try (ResultSet rs = st.executeQuery("SELECT * FROM " + tableName)) {
|
while (rs.next()) {
|
||||||
while (rs.next()) {
|
PlayerAuth auth = buildAuthFromResultSet(rs);
|
||||||
PlayerAuth pAuth = buildAuthFromResultSet(rs);
|
sqlExtension.extendAuth(auth, rs.getInt(col.ID), con);
|
||||||
if (hashAlgorithm == HashAlgorithm.XFBCRYPT) {
|
auths.add(auth);
|
||||||
try (PreparedStatement pst = con.prepareStatement("SELECT data FROM " + xfPrefix + "user_authenticate WHERE " + col.ID + "=?;")) {
|
|
||||||
int id = rs.getInt(col.ID);
|
|
||||||
pst.setInt(1, id);
|
|
||||||
ResultSet rs2 = pst.executeQuery();
|
|
||||||
if (rs2.next()) {
|
|
||||||
Blob blob = rs2.getBlob("data");
|
|
||||||
byte[] bytes = blob.getBytes(1, (int) blob.length());
|
|
||||||
pAuth.setPassword(new HashedPassword(XfBCrypt.getHashFromBlob(bytes)));
|
|
||||||
}
|
|
||||||
rs2.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auths.add(pAuth);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
@ -987,36 +677,6 @@ public class MySQL implements DataSource {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes a {@link ResultSet} safely.
|
|
||||||
*
|
|
||||||
* @param rs the result set to close
|
|
||||||
*/
|
|
||||||
private static void close(ResultSet rs) {
|
|
||||||
try {
|
|
||||||
if (rs != null && !rs.isClosed()) {
|
|
||||||
rs.close();
|
|
||||||
}
|
|
||||||
} catch (SQLException e) {
|
|
||||||
ConsoleLogger.logException("Could not close ResultSet", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes a {@link Statement} safely.
|
|
||||||
*
|
|
||||||
* @param st the statement set to close
|
|
||||||
*/
|
|
||||||
private static void close(Statement st) {
|
|
||||||
try {
|
|
||||||
if (st != null && !st.isClosed()) {
|
|
||||||
st.close();
|
|
||||||
}
|
|
||||||
} catch (SQLException e) {
|
|
||||||
ConsoleLogger.logException("Could not close Statement", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the last login column has a type that needs to be migrated.
|
* Checks if the last login column has a type that needs to be migrated.
|
||||||
*
|
*
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package fr.xephi.authme.datasource.converter;
|
|||||||
import fr.xephi.authme.datasource.DataSource;
|
import fr.xephi.authme.datasource.DataSource;
|
||||||
import fr.xephi.authme.datasource.DataSourceType;
|
import fr.xephi.authme.datasource.DataSourceType;
|
||||||
import fr.xephi.authme.datasource.MySQL;
|
import fr.xephi.authme.datasource.MySQL;
|
||||||
|
import fr.xephi.authme.datasource.mysqlextensions.MySqlExtensionsFactory;
|
||||||
import fr.xephi.authme.settings.Settings;
|
import fr.xephi.authme.settings.Settings;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
@ -14,15 +15,17 @@ import java.sql.SQLException;
|
|||||||
public class MySqlToSqlite extends AbstractDataSourceConverter<MySQL> {
|
public class MySqlToSqlite extends AbstractDataSourceConverter<MySQL> {
|
||||||
|
|
||||||
private final Settings settings;
|
private final Settings settings;
|
||||||
|
private final MySqlExtensionsFactory mySqlExtensionsFactory;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
MySqlToSqlite(DataSource dataSource, Settings settings) {
|
MySqlToSqlite(DataSource dataSource, Settings settings, MySqlExtensionsFactory mySqlExtensionsFactory) {
|
||||||
super(dataSource, DataSourceType.SQLITE);
|
super(dataSource, DataSourceType.SQLITE);
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
|
this.mySqlExtensionsFactory = mySqlExtensionsFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected MySQL getSource() throws SQLException, ClassNotFoundException {
|
protected MySQL getSource() throws SQLException {
|
||||||
return new MySQL(settings);
|
return new MySQL(settings, mySqlExtensionsFactory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,55 @@
|
|||||||
|
package fr.xephi.authme.datasource.mysqlextensions;
|
||||||
|
|
||||||
|
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||||
|
import fr.xephi.authme.datasource.Columns;
|
||||||
|
import fr.xephi.authme.settings.Settings;
|
||||||
|
import fr.xephi.authme.settings.properties.HooksSettings;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extension for IPB4.
|
||||||
|
*/
|
||||||
|
class Ipb4Extension extends MySqlExtension {
|
||||||
|
|
||||||
|
private final String ipbPrefix;
|
||||||
|
private final int ipbGroup;
|
||||||
|
|
||||||
|
Ipb4Extension(Settings settings, Columns col) {
|
||||||
|
super(settings, col);
|
||||||
|
this.ipbPrefix = settings.getProperty(HooksSettings.IPB_TABLE_PREFIX);
|
||||||
|
this.ipbGroup = settings.getProperty(HooksSettings.IPB_ACTIVATED_GROUP_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveAuth(PlayerAuth auth, Connection con) throws SQLException {
|
||||||
|
// Update player group in core_members
|
||||||
|
String sql = "UPDATE " + ipbPrefix + tableName
|
||||||
|
+ " SET " + tableName + ".member_group_id=? WHERE " + col.NAME + "=?;";
|
||||||
|
try (PreparedStatement pst2 = con.prepareStatement(sql)) {
|
||||||
|
pst2.setInt(1, ipbGroup);
|
||||||
|
pst2.setString(2, auth.getNickname());
|
||||||
|
pst2.executeUpdate();
|
||||||
|
}
|
||||||
|
// Get current time without ms
|
||||||
|
long time = System.currentTimeMillis() / 1000;
|
||||||
|
// update joined date
|
||||||
|
sql = "UPDATE " + ipbPrefix + tableName
|
||||||
|
+ " SET " + tableName + ".joined=? WHERE " + col.NAME + "=?;";
|
||||||
|
try (PreparedStatement pst2 = con.prepareStatement(sql)) {
|
||||||
|
pst2.setLong(1, time);
|
||||||
|
pst2.setString(2, auth.getNickname());
|
||||||
|
pst2.executeUpdate();
|
||||||
|
}
|
||||||
|
// Update last_visit
|
||||||
|
sql = "UPDATE " + ipbPrefix + tableName
|
||||||
|
+ " SET " + tableName + ".last_visit=? WHERE " + col.NAME + "=?;";
|
||||||
|
try (PreparedStatement pst2 = con.prepareStatement(sql)) {
|
||||||
|
pst2.setLong(1, time);
|
||||||
|
pst2.setString(2, auth.getNickname());
|
||||||
|
pst2.executeUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,96 @@
|
|||||||
|
package fr.xephi.authme.datasource.mysqlextensions;
|
||||||
|
|
||||||
|
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||||
|
import fr.xephi.authme.datasource.Columns;
|
||||||
|
import fr.xephi.authme.security.crypts.HashedPassword;
|
||||||
|
import fr.xephi.authme.settings.Settings;
|
||||||
|
import fr.xephi.authme.settings.properties.DatabaseSettings;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.OptionalInt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extension for the MySQL data source for forums. For certain password hashes (e.g. phpBB), we want
|
||||||
|
* to hook into the forum board and execute some actions specific to the forum software.
|
||||||
|
*/
|
||||||
|
public abstract class MySqlExtension {
|
||||||
|
|
||||||
|
protected final Columns col;
|
||||||
|
protected final String tableName;
|
||||||
|
|
||||||
|
MySqlExtension(Settings settings, Columns col) {
|
||||||
|
this.col = col;
|
||||||
|
this.tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs additional actions when a new player is saved.
|
||||||
|
*
|
||||||
|
* @param auth the player auth that has been saved
|
||||||
|
* @param con connection to the sql table
|
||||||
|
* @throws SQLException .
|
||||||
|
*/
|
||||||
|
public void saveAuth(PlayerAuth auth, Connection con) throws SQLException {
|
||||||
|
// extend for custom behavior
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes properties to the given PlayerAuth object that need to be retrieved in a specific manner
|
||||||
|
* when a PlayerAuth object is read from the table.
|
||||||
|
*
|
||||||
|
* @param auth the player auth object to extend
|
||||||
|
* @param id the database id of the player auth entry
|
||||||
|
* @param con connection to the sql table
|
||||||
|
* @throws SQLException .
|
||||||
|
*/
|
||||||
|
public void extendAuth(PlayerAuth auth, int id, Connection con) throws SQLException {
|
||||||
|
// extend for custom behavior
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs additional actions when a user's password is changed.
|
||||||
|
*
|
||||||
|
* @param user the name of the player (lowercase)
|
||||||
|
* @param password the new password to set
|
||||||
|
* @param con connection to the sql table
|
||||||
|
* @throws SQLException .
|
||||||
|
*/
|
||||||
|
public void changePassword(String user, HashedPassword password, Connection con) throws SQLException {
|
||||||
|
// extend for custom behavior
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs additional actions when a player is removed from the database.
|
||||||
|
*
|
||||||
|
* @param user the user to remove
|
||||||
|
* @param con connection to the sql table
|
||||||
|
* @throws SQLException .
|
||||||
|
*/
|
||||||
|
public void removeAuth(String user, Connection con) throws SQLException {
|
||||||
|
// extend for custom behavior
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the database ID of the given name from the database.
|
||||||
|
*
|
||||||
|
* @param name the name to get the ID for
|
||||||
|
* @param con connection to the sql table
|
||||||
|
* @return id of the playerAuth, or empty OptionalInt if the name is not registered
|
||||||
|
* @throws SQLException .
|
||||||
|
*/
|
||||||
|
protected OptionalInt retrieveIdFromTable(String name, Connection con) throws SQLException {
|
||||||
|
String sql = "SELECT " + col.ID + " FROM " + tableName + " WHERE " + col.NAME + "=?;";
|
||||||
|
try (PreparedStatement pst = con.prepareStatement(sql)) {
|
||||||
|
pst.setString(1, name);
|
||||||
|
try (ResultSet rs = pst.executeQuery()) {
|
||||||
|
if (rs.next()) {
|
||||||
|
return OptionalInt.of(rs.getInt(col.ID));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return OptionalInt.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
package fr.xephi.authme.datasource.mysqlextensions;
|
||||||
|
|
||||||
|
import fr.xephi.authme.datasource.Columns;
|
||||||
|
import fr.xephi.authme.security.HashAlgorithm;
|
||||||
|
import fr.xephi.authme.settings.Settings;
|
||||||
|
import fr.xephi.authme.settings.properties.SecuritySettings;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the appropriate {@link MySqlExtension}, depending on the configured password hashing algorithm.
|
||||||
|
*/
|
||||||
|
public class MySqlExtensionsFactory {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private Settings settings;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link MySqlExtension} object according to the configured hash algorithm.
|
||||||
|
*
|
||||||
|
* @param columnsConfig the columns configuration
|
||||||
|
* @return the extension the MySQL data source should use
|
||||||
|
*/
|
||||||
|
public MySqlExtension buildExtension(Columns columnsConfig) {
|
||||||
|
HashAlgorithm hash = settings.getProperty(SecuritySettings.PASSWORD_HASH);
|
||||||
|
switch (hash) {
|
||||||
|
case IPB4:
|
||||||
|
return new Ipb4Extension(settings, columnsConfig);
|
||||||
|
case PHPBB:
|
||||||
|
return new PhpBbExtension(settings, columnsConfig);
|
||||||
|
case WORDPRESS:
|
||||||
|
return new WordpressExtension(settings, columnsConfig);
|
||||||
|
case XFBCRYPT:
|
||||||
|
return new XfBcryptExtension(settings, columnsConfig);
|
||||||
|
default:
|
||||||
|
return new NoOpExtension(settings, columnsConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
package fr.xephi.authme.datasource.mysqlextensions;
|
||||||
|
|
||||||
|
import fr.xephi.authme.datasource.Columns;
|
||||||
|
import fr.xephi.authme.settings.Settings;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extension implementation that does not do anything.
|
||||||
|
*/
|
||||||
|
class NoOpExtension extends MySqlExtension {
|
||||||
|
|
||||||
|
NoOpExtension(Settings settings, Columns col) {
|
||||||
|
super(settings, col);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,83 @@
|
|||||||
|
package fr.xephi.authme.datasource.mysqlextensions;
|
||||||
|
|
||||||
|
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||||
|
import fr.xephi.authme.datasource.Columns;
|
||||||
|
import fr.xephi.authme.settings.Settings;
|
||||||
|
import fr.xephi.authme.settings.properties.HooksSettings;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.OptionalInt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extensions for phpBB when MySQL is used as data source.
|
||||||
|
*/
|
||||||
|
class PhpBbExtension extends MySqlExtension {
|
||||||
|
|
||||||
|
private final String phpBbPrefix;
|
||||||
|
private final int phpBbGroup;
|
||||||
|
|
||||||
|
PhpBbExtension(Settings settings, Columns col) {
|
||||||
|
super(settings, col);
|
||||||
|
this.phpBbPrefix = settings.getProperty(HooksSettings.PHPBB_TABLE_PREFIX);
|
||||||
|
this.phpBbGroup = settings.getProperty(HooksSettings.PHPBB_ACTIVATED_GROUP_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveAuth(PlayerAuth auth, Connection con) throws SQLException {
|
||||||
|
OptionalInt authId = retrieveIdFromTable(auth.getNickname(), con);
|
||||||
|
if (authId.isPresent()) {
|
||||||
|
updateSpecificsOnSave(authId.getAsInt(), auth.getNickname(), con);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateSpecificsOnSave(int id, String name, Connection con) throws SQLException {
|
||||||
|
// Insert player in phpbb_user_group
|
||||||
|
String sql = "INSERT INTO " + phpBbPrefix
|
||||||
|
+ "user_group (group_id, user_id, group_leader, user_pending) VALUES (?,?,?,?);";
|
||||||
|
try (PreparedStatement pst = con.prepareStatement(sql)) {
|
||||||
|
pst.setInt(1, phpBbGroup);
|
||||||
|
pst.setInt(2, id);
|
||||||
|
pst.setInt(3, 0);
|
||||||
|
pst.setInt(4, 0);
|
||||||
|
pst.executeUpdate();
|
||||||
|
}
|
||||||
|
// Update username_clean in phpbb_users
|
||||||
|
sql = "UPDATE " + tableName + " SET " + tableName + ".username_clean=? WHERE " + col.NAME + "=?;";
|
||||||
|
try (PreparedStatement pst = con.prepareStatement(sql)) {
|
||||||
|
pst.setString(1, name);
|
||||||
|
pst.setString(2, name);
|
||||||
|
pst.executeUpdate();
|
||||||
|
}
|
||||||
|
// Update player group in phpbb_users
|
||||||
|
sql = "UPDATE " + tableName + " SET " + tableName + ".group_id=? WHERE " + col.NAME + "=?;";
|
||||||
|
try (PreparedStatement pst = con.prepareStatement(sql)) {
|
||||||
|
pst.setInt(1, phpBbGroup);
|
||||||
|
pst.setString(2, name);
|
||||||
|
pst.executeUpdate();
|
||||||
|
}
|
||||||
|
// Get current time without ms
|
||||||
|
long time = System.currentTimeMillis() / 1000;
|
||||||
|
// Update user_regdate
|
||||||
|
sql = "UPDATE " + tableName + " SET " + tableName + ".user_regdate=? WHERE " + col.NAME + "=?;";
|
||||||
|
try (PreparedStatement pst = con.prepareStatement(sql)) {
|
||||||
|
pst.setLong(1, time);
|
||||||
|
pst.setString(2, name);
|
||||||
|
pst.executeUpdate();
|
||||||
|
}
|
||||||
|
// Update user_lastvisit
|
||||||
|
sql = "UPDATE " + tableName + " SET " + tableName + ".user_lastvisit=? WHERE " + col.NAME + "=?;";
|
||||||
|
try (PreparedStatement pst = con.prepareStatement(sql)) {
|
||||||
|
pst.setLong(1, time);
|
||||||
|
pst.setString(2, name);
|
||||||
|
pst.executeUpdate();
|
||||||
|
}
|
||||||
|
// Increment num_users
|
||||||
|
sql = "UPDATE " + phpBbPrefix
|
||||||
|
+ "config SET config_value = config_value + 1 WHERE config_name = 'num_users';";
|
||||||
|
try (PreparedStatement pst = con.prepareStatement(sql)) {
|
||||||
|
pst.executeUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,102 @@
|
|||||||
|
package fr.xephi.authme.datasource.mysqlextensions;
|
||||||
|
|
||||||
|
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||||
|
import fr.xephi.authme.datasource.Columns;
|
||||||
|
import fr.xephi.authme.settings.Settings;
|
||||||
|
import fr.xephi.authme.settings.properties.HooksSettings;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.OptionalInt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MySQL extensions for Wordpress.
|
||||||
|
*/
|
||||||
|
class WordpressExtension extends MySqlExtension {
|
||||||
|
|
||||||
|
private final String wordpressPrefix;
|
||||||
|
|
||||||
|
WordpressExtension(Settings settings, Columns col) {
|
||||||
|
super(settings, col);
|
||||||
|
this.wordpressPrefix = settings.getProperty(HooksSettings.WORDPRESS_TABLE_PREFIX);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveAuth(PlayerAuth auth, Connection con) throws SQLException {
|
||||||
|
OptionalInt authId = retrieveIdFromTable(auth.getNickname(), con);
|
||||||
|
if (authId.isPresent()) {
|
||||||
|
saveSpecifics(auth, authId.getAsInt(), con);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveSpecifics(PlayerAuth auth, int id, Connection con) throws SQLException {
|
||||||
|
String sql = "INSERT INTO " + wordpressPrefix + "usermeta (user_id, meta_key, meta_value) VALUES (?,?,?)";
|
||||||
|
try (PreparedStatement pst = con.prepareStatement(sql)) {
|
||||||
|
// First Name
|
||||||
|
pst.setInt(1, id);
|
||||||
|
pst.setString(2, "first_name");
|
||||||
|
pst.setString(3, "");
|
||||||
|
pst.addBatch();
|
||||||
|
// Last Name
|
||||||
|
pst.setInt(1, id);
|
||||||
|
pst.setString(2, "last_name");
|
||||||
|
pst.setString(3, "");
|
||||||
|
pst.addBatch();
|
||||||
|
// Nick Name
|
||||||
|
pst.setInt(1, id);
|
||||||
|
pst.setString(2, "nickname");
|
||||||
|
pst.setString(3, auth.getNickname());
|
||||||
|
pst.addBatch();
|
||||||
|
// Description
|
||||||
|
pst.setInt(1, id);
|
||||||
|
pst.setString(2, "description");
|
||||||
|
pst.setString(3, "");
|
||||||
|
pst.addBatch();
|
||||||
|
// Rich_Editing
|
||||||
|
pst.setInt(1, id);
|
||||||
|
pst.setString(2, "rich_editing");
|
||||||
|
pst.setString(3, "true");
|
||||||
|
pst.addBatch();
|
||||||
|
// Comments_Shortcuts
|
||||||
|
pst.setInt(1, id);
|
||||||
|
pst.setString(2, "comment_shortcuts");
|
||||||
|
pst.setString(3, "false");
|
||||||
|
pst.addBatch();
|
||||||
|
// admin_color
|
||||||
|
pst.setInt(1, id);
|
||||||
|
pst.setString(2, "admin_color");
|
||||||
|
pst.setString(3, "fresh");
|
||||||
|
pst.addBatch();
|
||||||
|
// use_ssl
|
||||||
|
pst.setInt(1, id);
|
||||||
|
pst.setString(2, "use_ssl");
|
||||||
|
pst.setString(3, "0");
|
||||||
|
pst.addBatch();
|
||||||
|
// show_admin_bar_front
|
||||||
|
pst.setInt(1, id);
|
||||||
|
pst.setString(2, "show_admin_bar_front");
|
||||||
|
pst.setString(3, "true");
|
||||||
|
pst.addBatch();
|
||||||
|
// wp_capabilities
|
||||||
|
pst.setInt(1, id);
|
||||||
|
pst.setString(2, wordpressPrefix + "capabilities");
|
||||||
|
pst.setString(3, "a:1:{s:10:\"subscriber\";b:1;}");
|
||||||
|
pst.addBatch();
|
||||||
|
// wp_user_level
|
||||||
|
pst.setInt(1, id);
|
||||||
|
pst.setString(2, wordpressPrefix + "user_level");
|
||||||
|
pst.setString(3, "0");
|
||||||
|
pst.addBatch();
|
||||||
|
// default_password_nag
|
||||||
|
pst.setInt(1, id);
|
||||||
|
pst.setString(2, "default_password_nag");
|
||||||
|
pst.setString(3, "");
|
||||||
|
pst.addBatch();
|
||||||
|
|
||||||
|
// Execute queries
|
||||||
|
pst.executeBatch();
|
||||||
|
pst.clearBatch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,140 @@
|
|||||||
|
package fr.xephi.authme.datasource.mysqlextensions;
|
||||||
|
|
||||||
|
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||||
|
import fr.xephi.authme.datasource.Columns;
|
||||||
|
import fr.xephi.authme.security.crypts.HashedPassword;
|
||||||
|
import fr.xephi.authme.security.crypts.XfBCrypt;
|
||||||
|
import fr.xephi.authme.settings.Settings;
|
||||||
|
import fr.xephi.authme.settings.properties.HooksSettings;
|
||||||
|
|
||||||
|
import java.sql.Blob;
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.OptionalInt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extension for XFBCRYPT.
|
||||||
|
*/
|
||||||
|
class XfBcryptExtension extends MySqlExtension {
|
||||||
|
|
||||||
|
private final String xfPrefix;
|
||||||
|
private final int xfGroup;
|
||||||
|
|
||||||
|
XfBcryptExtension(Settings settings, Columns col) {
|
||||||
|
super(settings, col);
|
||||||
|
this.xfPrefix = settings.getProperty(HooksSettings.XF_TABLE_PREFIX);
|
||||||
|
this.xfGroup = settings.getProperty(HooksSettings.XF_ACTIVATED_GROUP_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveAuth(PlayerAuth auth, Connection con) throws SQLException {
|
||||||
|
OptionalInt authId = retrieveIdFromTable(auth.getNickname(), con);
|
||||||
|
if (authId.isPresent()) {
|
||||||
|
updateXenforoTablesOnSave(auth, authId.getAsInt(), con);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateXenforoTablesOnSave(PlayerAuth auth, int id, Connection con) throws SQLException {
|
||||||
|
// Insert player password, salt in xf_user_authenticate
|
||||||
|
String sql = "INSERT INTO " + xfPrefix + "user_authenticate (user_id, scheme_class, data) VALUES (?,?,?)";
|
||||||
|
try (PreparedStatement pst = con.prepareStatement(sql)) {
|
||||||
|
pst.setInt(1, id);
|
||||||
|
pst.setString(2, XfBCrypt.SCHEME_CLASS);
|
||||||
|
String serializedHash = XfBCrypt.serializeHash(auth.getPassword().getHash());
|
||||||
|
byte[] bytes = serializedHash.getBytes();
|
||||||
|
Blob blob = con.createBlob();
|
||||||
|
blob.setBytes(1, bytes);
|
||||||
|
pst.setBlob(3, blob);
|
||||||
|
pst.executeUpdate();
|
||||||
|
}
|
||||||
|
// Update player group in xf_users
|
||||||
|
sql = "UPDATE " + tableName + " SET " + tableName + ".user_group_id=? WHERE " + col.NAME + "=?;";
|
||||||
|
try (PreparedStatement pst = con.prepareStatement(sql)) {
|
||||||
|
pst.setInt(1, xfGroup);
|
||||||
|
pst.setString(2, auth.getNickname());
|
||||||
|
pst.executeUpdate();
|
||||||
|
}
|
||||||
|
// Update player permission combination in xf_users
|
||||||
|
sql = "UPDATE " + tableName + " SET " + tableName + ".permission_combination_id=? WHERE " + col.NAME + "=?;";
|
||||||
|
try (PreparedStatement pst = con.prepareStatement(sql)) {
|
||||||
|
pst.setInt(1, xfGroup);
|
||||||
|
pst.setString(2, auth.getNickname());
|
||||||
|
pst.executeUpdate();
|
||||||
|
}
|
||||||
|
// Insert player privacy combination in xf_user_privacy
|
||||||
|
sql = "INSERT INTO " + xfPrefix + "user_privacy (user_id, allow_view_profile, allow_post_profile, "
|
||||||
|
+ "allow_send_personal_conversation, allow_view_identities, allow_receive_news_feed) VALUES (?,?,?,?,?,?)";
|
||||||
|
try (PreparedStatement pst = con.prepareStatement(sql)) {
|
||||||
|
pst.setInt(1, id);
|
||||||
|
pst.setString(2, "everyone");
|
||||||
|
pst.setString(3, "members");
|
||||||
|
pst.setString(4, "members");
|
||||||
|
pst.setString(5, "everyone");
|
||||||
|
pst.setString(6, "everyone");
|
||||||
|
pst.executeUpdate();
|
||||||
|
}
|
||||||
|
// Insert player group relation in xf_user_group_relation
|
||||||
|
sql = "INSERT INTO " + xfPrefix + "user_group_relation (user_id, user_group_id, is_primary) VALUES (?,?,?)";
|
||||||
|
try (PreparedStatement pst = con.prepareStatement(sql)) {
|
||||||
|
pst.setInt(1, id);
|
||||||
|
pst.setInt(2, xfGroup);
|
||||||
|
pst.setString(3, "1");
|
||||||
|
pst.executeUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void extendAuth(PlayerAuth auth, int id, Connection con) throws SQLException {
|
||||||
|
try (PreparedStatement pst = con.prepareStatement(
|
||||||
|
"SELECT data FROM " + xfPrefix + "user_authenticate WHERE " + col.ID + "=?;")) {
|
||||||
|
pst.setInt(1, id);
|
||||||
|
try (ResultSet rs = pst.executeQuery()) {
|
||||||
|
if (rs.next()) {
|
||||||
|
Blob blob = rs.getBlob("data");
|
||||||
|
byte[] bytes = blob.getBytes(1, (int) blob.length());
|
||||||
|
auth.setPassword(new HashedPassword(XfBCrypt.getHashFromBlob(bytes)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void changePassword(String user, HashedPassword password, Connection con) throws SQLException {
|
||||||
|
OptionalInt authId = retrieveIdFromTable(user, con);
|
||||||
|
if (authId.isPresent()) {
|
||||||
|
final int id = authId.getAsInt();
|
||||||
|
// Insert password in the correct table
|
||||||
|
String sql = "UPDATE " + xfPrefix + "user_authenticate SET data=? WHERE " + col.ID + "=?;";
|
||||||
|
try (PreparedStatement pst = con.prepareStatement(sql)) {
|
||||||
|
String serializedHash = XfBCrypt.serializeHash(password.getHash());
|
||||||
|
byte[] bytes = serializedHash.getBytes();
|
||||||
|
Blob blob = con.createBlob();
|
||||||
|
blob.setBytes(1, bytes);
|
||||||
|
pst.setBlob(1, blob);
|
||||||
|
pst.setInt(2, id);
|
||||||
|
pst.executeUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...
|
||||||
|
sql = "UPDATE " + xfPrefix + "user_authenticate SET scheme_class=? WHERE " + col.ID + "=?;";
|
||||||
|
try (PreparedStatement pst = con.prepareStatement(sql)) {
|
||||||
|
pst.setString(1, XfBCrypt.SCHEME_CLASS);
|
||||||
|
pst.setInt(2, id);
|
||||||
|
pst.executeUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAuth(String user, Connection con) throws SQLException {
|
||||||
|
OptionalInt authId = retrieveIdFromTable(user, con);
|
||||||
|
if (authId.isPresent()) {
|
||||||
|
String sql = "DELETE FROM " + xfPrefix + "user_authenticate WHERE " + col.ID + "=?;";
|
||||||
|
try (PreparedStatement xfDelete = con.prepareStatement(sql)) {
|
||||||
|
xfDelete.setInt(1, authId.getAsInt());
|
||||||
|
xfDelete.executeUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -9,6 +9,7 @@ import fr.xephi.authme.datasource.FlatFile;
|
|||||||
import fr.xephi.authme.datasource.MySQL;
|
import fr.xephi.authme.datasource.MySQL;
|
||||||
import fr.xephi.authme.datasource.SQLite;
|
import fr.xephi.authme.datasource.SQLite;
|
||||||
import fr.xephi.authme.datasource.converter.ForceFlatToSqlite;
|
import fr.xephi.authme.datasource.converter.ForceFlatToSqlite;
|
||||||
|
import fr.xephi.authme.datasource.mysqlextensions.MySqlExtensionsFactory;
|
||||||
import fr.xephi.authme.service.BukkitService;
|
import fr.xephi.authme.service.BukkitService;
|
||||||
import fr.xephi.authme.settings.Settings;
|
import fr.xephi.authme.settings.Settings;
|
||||||
import fr.xephi.authme.settings.properties.DatabaseSettings;
|
import fr.xephi.authme.settings.properties.DatabaseSettings;
|
||||||
@ -36,6 +37,8 @@ public class DataSourceProvider implements Provider<DataSource> {
|
|||||||
private BukkitService bukkitService;
|
private BukkitService bukkitService;
|
||||||
@Inject
|
@Inject
|
||||||
private PlayerCache playerCache;
|
private PlayerCache playerCache;
|
||||||
|
@Inject
|
||||||
|
private MySqlExtensionsFactory mySqlExtensionsFactory;
|
||||||
|
|
||||||
DataSourceProvider() {
|
DataSourceProvider() {
|
||||||
}
|
}
|
||||||
@ -67,7 +70,7 @@ public class DataSourceProvider implements Provider<DataSource> {
|
|||||||
dataSource = new FlatFile(source);
|
dataSource = new FlatFile(source);
|
||||||
break;
|
break;
|
||||||
case MYSQL:
|
case MYSQL:
|
||||||
dataSource = new MySQL(settings);
|
dataSource = new MySQL(settings, mySqlExtensionsFactory);
|
||||||
break;
|
break;
|
||||||
case SQLITE:
|
case SQLITE:
|
||||||
dataSource = new SQLite(settings);
|
dataSource = new SQLite(settings);
|
||||||
@ -82,12 +85,12 @@ public class DataSourceProvider implements Provider<DataSource> {
|
|||||||
dataSource = new CacheDataSource(dataSource, playerCache);
|
dataSource = new CacheDataSource(dataSource, playerCache);
|
||||||
}
|
}
|
||||||
if (DataSourceType.SQLITE.equals(dataSourceType)) {
|
if (DataSourceType.SQLITE.equals(dataSourceType)) {
|
||||||
checkDataSourceSize(dataSource, bukkitService);
|
checkDataSourceSize(dataSource);
|
||||||
}
|
}
|
||||||
return dataSource;
|
return dataSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkDataSourceSize(final DataSource dataSource, BukkitService bukkitService) {
|
private void checkDataSourceSize(DataSource dataSource) {
|
||||||
bukkitService.runTaskAsynchronously(() -> {
|
bukkitService.runTaskAsynchronously(() -> {
|
||||||
int accounts = dataSource.getAccountsRegistered();
|
int accounts = dataSource.getAccountsRegistered();
|
||||||
if (accounts >= SQLITE_MAX_SIZE) {
|
if (accounts >= SQLITE_MAX_SIZE) {
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import com.google.common.collect.ImmutableList;
|
|||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import fr.xephi.authme.datasource.Columns;
|
import fr.xephi.authme.datasource.Columns;
|
||||||
|
import fr.xephi.authme.datasource.mysqlextensions.MySqlExtension;
|
||||||
import fr.xephi.authme.initialization.HasCleanup;
|
import fr.xephi.authme.initialization.HasCleanup;
|
||||||
import fr.xephi.authme.process.register.executors.RegistrationMethod;
|
import fr.xephi.authme.process.register.executors.RegistrationMethod;
|
||||||
import fr.xephi.authme.security.crypts.Whirlpool;
|
import fr.xephi.authme.security.crypts.Whirlpool;
|
||||||
@ -55,6 +56,7 @@ public class ClassesConsistencyTest {
|
|||||||
/** Classes excluded from the field visibility test. */
|
/** Classes excluded from the field visibility test. */
|
||||||
private static final Set<Class<?>> CLASSES_EXCLUDED_FROM_VISIBILITY_TEST = ImmutableSet.of(
|
private static final Set<Class<?>> CLASSES_EXCLUDED_FROM_VISIBILITY_TEST = ImmutableSet.of(
|
||||||
Whirlpool.class, // not our implementation, so we don't touch it
|
Whirlpool.class, // not our implementation, so we don't touch it
|
||||||
|
MySqlExtension.class, // has immutable protected fields used by all children
|
||||||
Columns.class // uses non-static String constants, which is safe
|
Columns.class // uses non-static String constants, which is safe
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
package fr.xephi.authme;
|
package fr.xephi.authme;
|
||||||
|
|
||||||
|
import ch.jalu.configme.properties.Property;
|
||||||
import fr.xephi.authme.service.BukkitService;
|
import fr.xephi.authme.service.BukkitService;
|
||||||
|
import fr.xephi.authme.settings.Settings;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
@ -18,6 +20,7 @@ import java.nio.file.Path;
|
|||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyLong;
|
import static org.mockito.ArgumentMatchers.anyLong;
|
||||||
import static org.mockito.BDDMockito.given;
|
import static org.mockito.BDDMockito.given;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
@ -206,4 +209,14 @@ public final class TestHelper {
|
|||||||
given(player.getAddress()).willReturn(inetSocketAddress);
|
given(player.getAddress()).willReturn(inetSocketAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures the Settings mock to return the property's default value for any given property.
|
||||||
|
*
|
||||||
|
* @param settings the settings mock
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static void returnDefaultsForAllProperties(Settings settings) {
|
||||||
|
given(settings.getProperty(any(Property.class)))
|
||||||
|
.willAnswer(invocation -> ((Property<?>) invocation.getArgument(0)).getDefaultValue());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,17 +1,10 @@
|
|||||||
package fr.xephi.authme.datasource;
|
package fr.xephi.authme.datasource;
|
||||||
|
|
||||||
import ch.jalu.configme.properties.Property;
|
|
||||||
import com.google.common.base.MoreObjects;
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
import fr.xephi.authme.TestHelper;
|
import fr.xephi.authme.TestHelper;
|
||||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||||
import fr.xephi.authme.security.HashAlgorithm;
|
|
||||||
import fr.xephi.authme.security.crypts.HashedPassword;
|
import fr.xephi.authme.security.crypts.HashedPassword;
|
||||||
import fr.xephi.authme.settings.Settings;
|
|
||||||
import fr.xephi.authme.settings.properties.SecuritySettings;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
@ -19,7 +12,6 @@ import org.junit.runners.Parameterized;
|
|||||||
import org.mockito.invocation.InvocationOnMock;
|
import org.mockito.invocation.InvocationOnMock;
|
||||||
import org.mockito.stubbing.Answer;
|
import org.mockito.stubbing.Answer;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.ParameterizedType;
|
import java.lang.reflect.ParameterizedType;
|
||||||
@ -37,7 +29,6 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
|
||||||
import static org.mockito.ArgumentMatchers.anyInt;
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
import static org.mockito.ArgumentMatchers.anyLong;
|
import static org.mockito.ArgumentMatchers.anyLong;
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
@ -46,124 +37,57 @@ import static org.mockito.Mockito.mock;
|
|||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test class which runs through a datasource implementation and verifies that all
|
* Test class which runs through objects interacting with a database and verifies that all
|
||||||
* instances of {@link AutoCloseable} that are created in the calls are closed again.
|
* instances of {@link AutoCloseable} that are created in the calls are closed again.
|
||||||
* <p>
|
* <p>
|
||||||
* Instead of an actual connection to a datasource, we pass a mock Connection object
|
* Instead of an actual connection to a datasource, we pass a mock Connection object
|
||||||
* which is set to create additional mocks on demand for Statement and ResultSet objects.
|
* which is set to create additional mocks on demand for Statement and ResultSet objects.
|
||||||
* This test ensures that all such objects that are created will be closed again by
|
* This test ensures that all such objects that are created will be closed again by
|
||||||
* keeping a list of mocks ({@link #closeables}) and then verifying that all have been
|
* keeping a list of mocks ({@link #closeables}) and then verifying that all have been
|
||||||
* closed {@link #verifyHaveMocksBeenClosed()}.
|
* closed ({@link #verifyHaveMocksBeenClosed()}).
|
||||||
*/
|
*/
|
||||||
@RunWith(Parameterized.class)
|
@RunWith(Parameterized.class)
|
||||||
public abstract class AbstractResourceClosingTest {
|
public abstract class AbstractResourceClosingTest {
|
||||||
|
|
||||||
/** List of DataSource method names not to test. */
|
|
||||||
private static final Set<String> IGNORED_METHODS = ImmutableSet.of("reload", "close", "getType");
|
|
||||||
|
|
||||||
/** Collection of values to use to call methods with the parameters they expect. */
|
/** Collection of values to use to call methods with the parameters they expect. */
|
||||||
private static final Map<Class<?>, Object> PARAM_VALUES = getDefaultParameters();
|
private static final Map<Class<?>, Object> PARAM_VALUES = getDefaultParameters();
|
||||||
|
|
||||||
/**
|
|
||||||
* Custom list of hash algorithms to use to test a method. By default we define {@link HashAlgorithm#XFBCRYPT} as
|
|
||||||
* algorithms we use as a lot of methods execute additional statements in {@link MySQL}. If other algorithms
|
|
||||||
* have custom behaviors, they can be supplied in this map so it will be tested as well.
|
|
||||||
*/
|
|
||||||
private static final Map<String, HashAlgorithm[]> CUSTOM_ALGORITHMS = getCustomAlgorithmList();
|
|
||||||
|
|
||||||
/** Mock of a settings instance. */
|
|
||||||
private static Settings settings;
|
|
||||||
|
|
||||||
/** The datasource to test. */
|
|
||||||
private DataSource dataSource;
|
|
||||||
|
|
||||||
/** The DataSource method to test. */
|
/** The DataSource method to test. */
|
||||||
private Method method;
|
private Method method;
|
||||||
|
|
||||||
/** Keeps track of the closeables which are created during the tested call. */
|
/** Keeps track of the closeables which are created during the tested call. */
|
||||||
private List<AutoCloseable> closeables = new ArrayList<>();
|
private List<AutoCloseable> closeables = new ArrayList<>();
|
||||||
|
|
||||||
|
private boolean hasCreatedConnection = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for the test instance verifying the given method with the given hash algorithm.
|
* Constructor for the test instance verifying the given method.
|
||||||
*
|
*
|
||||||
* @param method The DataSource method to test
|
* @param method The DataSource method to test
|
||||||
* @param name The name of the method
|
* @param name The name of the method
|
||||||
* @param algorithm The hash algorithm to use
|
|
||||||
*/
|
*/
|
||||||
public AbstractResourceClosingTest(Method method, String name, HashAlgorithm algorithm) {
|
public AbstractResourceClosingTest(Method method, String name) {
|
||||||
// Note ljacqu 20160227: The name parameter is necessary as we pass it from the @Parameters method;
|
// Note ljacqu 20160227: The name parameter is necessary as we pass it from the @Parameters method;
|
||||||
// we use the method name in the annotation to name the test sensibly
|
// we use the method name in the annotation to name the test sensibly
|
||||||
this.method = method;
|
this.method = method;
|
||||||
given(settings.getProperty(SecuritySettings.PASSWORD_HASH)).willReturn(algorithm);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Initialize the settings mock and makes it return the default of any given property by default. */
|
|
||||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void initializeSettings() throws IOException, ClassNotFoundException {
|
public static void initializeLogger() {
|
||||||
settings = mock(Settings.class);
|
|
||||||
given(settings.getProperty(any(Property.class))).willAnswer(new Answer() {
|
|
||||||
@Override
|
|
||||||
public Object answer(InvocationOnMock invocation) {
|
|
||||||
return ((Property<?>) invocation.getArguments()[0]).getDefaultValue();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
TestHelper.setupLogger();
|
TestHelper.setupLogger();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Initialize the dataSource implementation to test based on a mock connection. */
|
|
||||||
@Before
|
|
||||||
public void setUpMockConnection() throws Exception {
|
|
||||||
Connection connection = initConnection();
|
|
||||||
dataSource = createDataSource(settings, connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The actual test -- executes the method given through the constructor and then verifies that all
|
* The actual test -- executes the method given through the constructor and then verifies that all
|
||||||
* AutoCloseable mocks it constructed have been closed.
|
* AutoCloseable mocks it constructed have been closed.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void shouldCloseResources() throws IllegalAccessException, InvocationTargetException {
|
public void shouldCloseResources() throws IllegalAccessException, InvocationTargetException {
|
||||||
method.invoke(dataSource, buildParamListForMethod(method));
|
method.invoke(getObjectUnderTest(), buildParamListForMethod(method));
|
||||||
verifyHaveMocksBeenClosed();
|
verifyHaveMocksBeenClosed();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
protected abstract Object getObjectUnderTest();
|
||||||
* Initialization method -- provides the parameters to run the test with by scanning all DataSource
|
|
||||||
* methods. By default, we run one test per method with the default hash algorithm, XFBCRYPT.
|
|
||||||
* If the map of custom algorithms has an entry for the method name, we add an entry for each algorithm
|
|
||||||
* supplied by the map.
|
|
||||||
*
|
|
||||||
* @return Test parameters
|
|
||||||
*/
|
|
||||||
@Parameterized.Parameters(name = "{1}({2})")
|
|
||||||
public static Collection<Object[]> data() {
|
|
||||||
List<Method> methods = getDataSourceMethods();
|
|
||||||
List<Object[]> data = new ArrayList<>();
|
|
||||||
// Use XFBCRYPT if nothing else specified as there is a lot of specific behavior to this hash algorithm in MySQL
|
|
||||||
final HashAlgorithm[] defaultAlgorithm = new HashAlgorithm[]{HashAlgorithm.XFBCRYPT};
|
|
||||||
for (Method method : methods) {
|
|
||||||
HashAlgorithm[] algorithms = MoreObjects.firstNonNull(CUSTOM_ALGORITHMS.get(method.getName()), defaultAlgorithm);
|
|
||||||
for (HashAlgorithm algorithm : algorithms) {
|
|
||||||
data.add(new Object[]{method, method.getName(), algorithm});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create a DataSource instance with the given mock settings and mock connection. */
|
|
||||||
protected abstract DataSource createDataSource(Settings settings, Connection connection) throws Exception;
|
|
||||||
|
|
||||||
/* Get all methods of the DataSource interface, minus the ones in the ignored list. */
|
|
||||||
private static List<Method> getDataSourceMethods() {
|
|
||||||
List<Method> publicMethods = new ArrayList<>();
|
|
||||||
for (Method method : DataSource.class.getDeclaredMethods()) {
|
|
||||||
if (!IGNORED_METHODS.contains(method.getName())) {
|
|
||||||
publicMethods.add(method);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return publicMethods;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify that all AutoCloseables that have been created during the method execution have been closed.
|
* Verify that all AutoCloseables that have been created during the method execution have been closed.
|
||||||
@ -187,7 +111,7 @@ public abstract class AbstractResourceClosingTest {
|
|||||||
* @param method The method to create a valid parameter list for
|
* @param method The method to create a valid parameter list for
|
||||||
* @return Parameter list to invoke the given method with
|
* @return Parameter list to invoke the given method with
|
||||||
*/
|
*/
|
||||||
private static Object[] buildParamListForMethod(Method method) {
|
private Object[] buildParamListForMethod(Method method) {
|
||||||
List<Object> params = new ArrayList<>();
|
List<Object> params = new ArrayList<>();
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for (Class<?> paramType : method.getParameterTypes()) {
|
for (Class<?> paramType : method.getParameterTypes()) {
|
||||||
@ -195,7 +119,7 @@ public abstract class AbstractResourceClosingTest {
|
|||||||
// but that is a sensible assumption and makes our life much easier later on when juggling with Type
|
// but that is a sensible assumption and makes our life much easier later on when juggling with Type
|
||||||
Object param = Collection.class.isAssignableFrom(paramType)
|
Object param = Collection.class.isAssignableFrom(paramType)
|
||||||
? getTypedCollection(method.getGenericParameterTypes()[index])
|
? getTypedCollection(method.getGenericParameterTypes()[index])
|
||||||
: PARAM_VALUES.get(paramType);
|
: getMethodParameter(paramType);
|
||||||
Preconditions.checkNotNull(param, "No param type for " + paramType);
|
Preconditions.checkNotNull(param, "No param type for " + paramType);
|
||||||
params.add(param);
|
params.add(param);
|
||||||
++index;
|
++index;
|
||||||
@ -203,6 +127,15 @@ public abstract class AbstractResourceClosingTest {
|
|||||||
return params.toArray();
|
return params.toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Object getMethodParameter(Class<?> paramType) {
|
||||||
|
if (paramType.equals(Connection.class)) {
|
||||||
|
Preconditions.checkArgument(!hasCreatedConnection, "A Connection object was already created in this test run");
|
||||||
|
hasCreatedConnection = true;
|
||||||
|
return initConnection();
|
||||||
|
}
|
||||||
|
return PARAM_VALUES.get(paramType);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a collection of the required type with some test elements that correspond to the
|
* Return a collection of the required type with some test elements that correspond to the
|
||||||
* collection's generic type.
|
* collection's generic type.
|
||||||
@ -247,29 +180,15 @@ public abstract class AbstractResourceClosingTest {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the custom list of hash algorithms to test a method with to execute code specific to
|
|
||||||
* one hash algorithm. By default, XFBCRYPT is used. Only MySQL has code specific to algorithms
|
|
||||||
* but for technical reasons the custom list will be used for all tested classes.
|
|
||||||
*
|
|
||||||
* @return List of custom algorithms by method
|
|
||||||
*/
|
|
||||||
private static Map<String, HashAlgorithm[]> getCustomAlgorithmList() {
|
|
||||||
// We use XFBCRYPT as default encryption method so we don't have to list many of the special cases for it
|
|
||||||
return ImmutableMap.<String, HashAlgorithm[]>builder()
|
|
||||||
.put("saveAuth", new HashAlgorithm[]{HashAlgorithm.PHPBB, HashAlgorithm.WORDPRESS})
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------
|
// ---------------------
|
||||||
// Mock initialization
|
// Mock initialization
|
||||||
// ---------------------
|
// ---------------------
|
||||||
/**
|
/**
|
||||||
* Initialize the connection mock which produces additional AutoCloseable mocks and records them.
|
* Initializes the connection mock which produces additional AutoCloseable mocks and records them.
|
||||||
*
|
*
|
||||||
* @return Connection mock
|
* @return Connection mock
|
||||||
*/
|
*/
|
||||||
private Connection initConnection() {
|
protected Connection initConnection() {
|
||||||
Connection connection = mock(Connection.class);
|
Connection connection = mock(Connection.class);
|
||||||
try {
|
try {
|
||||||
given(connection.prepareStatement(anyString())).willAnswer(preparedStatementAnswer());
|
given(connection.prepareStatement(anyString())).willAnswer(preparedStatementAnswer());
|
||||||
|
|||||||
@ -0,0 +1,75 @@
|
|||||||
|
package fr.xephi.authme.datasource;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import fr.xephi.authme.TestHelper;
|
||||||
|
import fr.xephi.authme.settings.Settings;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.runners.Parameterized;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resource-closing test for SQL data sources.
|
||||||
|
*/
|
||||||
|
public abstract class AbstractSqlDataSourceResourceClosingTest extends AbstractResourceClosingTest {
|
||||||
|
|
||||||
|
/** List of DataSource method names not to test. */
|
||||||
|
private static final Set<String> IGNORED_METHODS = ImmutableSet.of("reload", "getType");
|
||||||
|
|
||||||
|
private static Settings settings;
|
||||||
|
|
||||||
|
AbstractSqlDataSourceResourceClosingTest(Method method, String name) {
|
||||||
|
super(method, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void initializeSettings() {
|
||||||
|
settings = mock(Settings.class);
|
||||||
|
TestHelper.returnDefaultsForAllProperties(settings);
|
||||||
|
TestHelper.setupLogger();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected DataSource getObjectUnderTest() {
|
||||||
|
try {
|
||||||
|
return createDataSource(settings, initConnection());
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IllegalStateException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create a DataSource instance with the given mock settings and mock connection. */
|
||||||
|
protected abstract DataSource createDataSource(Settings settings, Connection connection) throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialization method -- provides the parameters to run the test with by scanning all DataSource methods.
|
||||||
|
*
|
||||||
|
* @return Test parameters
|
||||||
|
*/
|
||||||
|
@Parameterized.Parameters(name = "{1}")
|
||||||
|
public static Collection<Object[]> data() {
|
||||||
|
List<Method> methods = getDataSourceMethods();
|
||||||
|
List<Object[]> data = new ArrayList<>();
|
||||||
|
for (Method method : methods) {
|
||||||
|
data.add(new Object[]{method, method.getName()});
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get all methods of the DataSource interface, minus the ones in the ignored list. */
|
||||||
|
private static List<Method> getDataSourceMethods() {
|
||||||
|
List<Method> publicMethods = new ArrayList<>();
|
||||||
|
for (Method method : DataSource.class.getDeclaredMethods()) {
|
||||||
|
if (!IGNORED_METHODS.contains(method.getName())) {
|
||||||
|
publicMethods.add(method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return publicMethods;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,13 +4,13 @@ import ch.jalu.configme.properties.Property;
|
|||||||
import com.zaxxer.hikari.HikariConfig;
|
import com.zaxxer.hikari.HikariConfig;
|
||||||
import com.zaxxer.hikari.HikariDataSource;
|
import com.zaxxer.hikari.HikariDataSource;
|
||||||
import fr.xephi.authme.TestHelper;
|
import fr.xephi.authme.TestHelper;
|
||||||
|
import fr.xephi.authme.datasource.mysqlextensions.MySqlExtension;
|
||||||
|
import fr.xephi.authme.datasource.mysqlextensions.MySqlExtensionsFactory;
|
||||||
import fr.xephi.authme.settings.Settings;
|
import fr.xephi.authme.settings.Settings;
|
||||||
import fr.xephi.authme.settings.properties.DatabaseSettings;
|
import fr.xephi.authme.settings.properties.DatabaseSettings;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.mockito.invocation.InvocationOnMock;
|
|
||||||
import org.mockito.stubbing.Answer;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
@ -30,6 +30,8 @@ public class MySqlIntegrationTest extends AbstractDataSourceIntegrationTest {
|
|||||||
|
|
||||||
/** Mock of a settings instance. */
|
/** Mock of a settings instance. */
|
||||||
private static Settings settings;
|
private static Settings settings;
|
||||||
|
/** Mock of extensions factory. */
|
||||||
|
private static MySqlExtensionsFactory extensionsFactory;
|
||||||
/** SQL statement to execute before running a test. */
|
/** SQL statement to execute before running a test. */
|
||||||
private static String sqlInitialize;
|
private static String sqlInitialize;
|
||||||
/** Connection to the H2 test database. */
|
/** Connection to the H2 test database. */
|
||||||
@ -38,19 +40,15 @@ public class MySqlIntegrationTest extends AbstractDataSourceIntegrationTest {
|
|||||||
/**
|
/**
|
||||||
* Set up the settings mock to return specific values for database settings and load {@link #sqlInitialize}.
|
* Set up the settings mock to return specific values for database settings and load {@link #sqlInitialize}.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void initializeSettings() throws IOException, ClassNotFoundException {
|
public static void initializeSettings() throws IOException, ClassNotFoundException {
|
||||||
// Check that we have an H2 driver
|
// Check that we have an H2 driver
|
||||||
Class.forName("org.h2.jdbcx.JdbcDataSource");
|
Class.forName("org.h2.jdbcx.JdbcDataSource");
|
||||||
|
|
||||||
settings = mock(Settings.class);
|
settings = mock(Settings.class);
|
||||||
when(settings.getProperty(any(Property.class))).thenAnswer(new Answer() {
|
TestHelper.returnDefaultsForAllProperties(settings);
|
||||||
@Override
|
extensionsFactory = mock(MySqlExtensionsFactory.class);
|
||||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
when(extensionsFactory.buildExtension(any(Columns.class))).thenReturn(mock(MySqlExtension.class));
|
||||||
return ((Property) invocation.getArguments()[0]).getDefaultValue();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
set(DatabaseSettings.MYSQL_DATABASE, "h2_test");
|
set(DatabaseSettings.MYSQL_DATABASE, "h2_test");
|
||||||
set(DatabaseSettings.MYSQL_TABLE, "authme");
|
set(DatabaseSettings.MYSQL_TABLE, "authme");
|
||||||
TestHelper.setRealLogger();
|
TestHelper.setRealLogger();
|
||||||
@ -85,7 +83,7 @@ public class MySqlIntegrationTest extends AbstractDataSourceIntegrationTest {
|
|||||||
@Override
|
@Override
|
||||||
protected DataSource getDataSource(String saltColumn) {
|
protected DataSource getDataSource(String saltColumn) {
|
||||||
when(settings.getProperty(DatabaseSettings.MYSQL_COL_SALT)).thenReturn(saltColumn);
|
when(settings.getProperty(DatabaseSettings.MYSQL_COL_SALT)).thenReturn(saltColumn);
|
||||||
return new MySQL(settings, hikariSource);
|
return new MySQL(settings, hikariSource, extensionsFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <T> void set(Property<T> property, T value) {
|
private static <T> void set(Property<T> property, T value) {
|
||||||
|
|||||||
@ -1,29 +1,33 @@
|
|||||||
package fr.xephi.authme.datasource;
|
package fr.xephi.authme.datasource;
|
||||||
|
|
||||||
import com.zaxxer.hikari.HikariDataSource;
|
import com.zaxxer.hikari.HikariDataSource;
|
||||||
import fr.xephi.authme.security.HashAlgorithm;
|
import fr.xephi.authme.datasource.mysqlextensions.MySqlExtension;
|
||||||
|
import fr.xephi.authme.datasource.mysqlextensions.MySqlExtensionsFactory;
|
||||||
import fr.xephi.authme.settings.Settings;
|
import fr.xephi.authme.settings.Settings;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.BDDMockito.given;
|
import static org.mockito.BDDMockito.given;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resource closing test for {@link MySQL}.
|
* Resource closing test for {@link MySQL}.
|
||||||
*/
|
*/
|
||||||
public class MySqlResourceClosingTest extends AbstractResourceClosingTest {
|
public class MySqlResourceClosingTest extends AbstractSqlDataSourceResourceClosingTest {
|
||||||
|
|
||||||
public MySqlResourceClosingTest(Method method, String name, HashAlgorithm algorithm) {
|
public MySqlResourceClosingTest(Method method, String name) {
|
||||||
super(method, name, algorithm);
|
super(method, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected DataSource createDataSource(Settings settings, Connection connection) throws Exception {
|
protected DataSource createDataSource(Settings settings, Connection connection) throws Exception {
|
||||||
HikariDataSource hikariDataSource = mock(HikariDataSource.class);
|
HikariDataSource hikariDataSource = mock(HikariDataSource.class);
|
||||||
given(hikariDataSource.getConnection()).willReturn(connection);
|
given(hikariDataSource.getConnection()).willReturn(connection);
|
||||||
return new MySQL(settings, hikariDataSource);
|
MySqlExtensionsFactory extensionsFactory = mock(MySqlExtensionsFactory.class);
|
||||||
|
given(extensionsFactory.buildExtension(any(Columns.class))).willReturn(mock(MySqlExtension.class));
|
||||||
|
return new MySQL(settings, hikariDataSource, extensionsFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,8 +9,6 @@ import org.junit.After;
|
|||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mockito.invocation.InvocationOnMock;
|
|
||||||
import org.mockito.stubbing.Answer;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
@ -22,7 +20,6 @@ import java.sql.Statement;
|
|||||||
|
|
||||||
import static org.hamcrest.Matchers.hasSize;
|
import static org.hamcrest.Matchers.hasSize;
|
||||||
import static org.junit.Assert.assertThat;
|
import static org.junit.Assert.assertThat;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
@ -41,19 +38,13 @@ public class SQLiteIntegrationTest extends AbstractDataSourceIntegrationTest {
|
|||||||
/**
|
/**
|
||||||
* Set up the settings mock to return specific values for database settings and load {@link #sqlInitialize}.
|
* Set up the settings mock to return specific values for database settings and load {@link #sqlInitialize}.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void initializeSettings() throws IOException, ClassNotFoundException {
|
public static void initializeSettings() throws IOException, ClassNotFoundException {
|
||||||
// Check that we have an implementation for SQLite
|
// Check that we have an implementation for SQLite
|
||||||
Class.forName("org.sqlite.JDBC");
|
Class.forName("org.sqlite.JDBC");
|
||||||
|
|
||||||
settings = mock(Settings.class);
|
settings = mock(Settings.class);
|
||||||
when(settings.getProperty(any(Property.class))).thenAnswer(new Answer() {
|
TestHelper.returnDefaultsForAllProperties(settings);
|
||||||
@Override
|
|
||||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
|
||||||
return ((Property) invocation.getArguments()[0]).getDefaultValue();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
set(DatabaseSettings.MYSQL_DATABASE, "sqlite-test");
|
set(DatabaseSettings.MYSQL_DATABASE, "sqlite-test");
|
||||||
set(DatabaseSettings.MYSQL_TABLE, "authme");
|
set(DatabaseSettings.MYSQL_TABLE, "authme");
|
||||||
TestHelper.setRealLogger();
|
TestHelper.setRealLogger();
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
package fr.xephi.authme.datasource;
|
package fr.xephi.authme.datasource;
|
||||||
|
|
||||||
import fr.xephi.authme.security.HashAlgorithm;
|
|
||||||
import fr.xephi.authme.settings.Settings;
|
import fr.xephi.authme.settings.Settings;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
@ -9,10 +8,10 @@ import java.sql.Connection;
|
|||||||
/**
|
/**
|
||||||
* Resource closing test for {@link SQLite}.
|
* Resource closing test for {@link SQLite}.
|
||||||
*/
|
*/
|
||||||
public class SQLiteResourceClosingTest extends AbstractResourceClosingTest {
|
public class SQLiteResourceClosingTest extends AbstractSqlDataSourceResourceClosingTest {
|
||||||
|
|
||||||
public SQLiteResourceClosingTest(Method method, String name, HashAlgorithm algorithm) {
|
public SQLiteResourceClosingTest(Method method, String name) {
|
||||||
super(method, name, algorithm);
|
super(method, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -0,0 +1,51 @@
|
|||||||
|
package fr.xephi.authme.datasource.mysqlextensions;
|
||||||
|
|
||||||
|
import fr.xephi.authme.TestHelper;
|
||||||
|
import fr.xephi.authme.datasource.AbstractResourceClosingTest;
|
||||||
|
import fr.xephi.authme.datasource.Columns;
|
||||||
|
import fr.xephi.authme.settings.Settings;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.runners.Parameterized;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that SQL resources are closed properly in {@link MySqlExtension} implementations.
|
||||||
|
*/
|
||||||
|
public abstract class AbstractMySqlExtensionResourceClosingTest extends AbstractResourceClosingTest {
|
||||||
|
|
||||||
|
private static Settings settings;
|
||||||
|
private static Columns columns;
|
||||||
|
|
||||||
|
public AbstractMySqlExtensionResourceClosingTest(Method method, String name) {
|
||||||
|
super(method, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void initSettings() {
|
||||||
|
settings = mock(Settings.class);
|
||||||
|
TestHelper.returnDefaultsForAllProperties(settings);
|
||||||
|
columns = new Columns(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected MySqlExtension getObjectUnderTest() {
|
||||||
|
return createExtension(settings, columns);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract MySqlExtension createExtension(Settings settings, Columns columns);
|
||||||
|
|
||||||
|
@Parameterized.Parameters(name = "{1}")
|
||||||
|
public static List<Object[]> createParameters() {
|
||||||
|
return Arrays.stream(MySqlExtension.class.getDeclaredMethods())
|
||||||
|
.filter(m -> Modifier.isPublic(m.getModifiers()))
|
||||||
|
.map(m -> new Object[]{m, m.getName()})
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
package fr.xephi.authme.datasource.mysqlextensions;
|
||||||
|
|
||||||
|
import fr.xephi.authme.datasource.Columns;
|
||||||
|
import fr.xephi.authme.settings.Settings;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resource closing test for {@link Ipb4Extension}.
|
||||||
|
*/
|
||||||
|
public class Ipb4ExtensionResourceClosingTest extends AbstractMySqlExtensionResourceClosingTest {
|
||||||
|
|
||||||
|
public Ipb4ExtensionResourceClosingTest(Method method, String name) {
|
||||||
|
super(method, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected MySqlExtension createExtension(Settings settings, Columns columns) {
|
||||||
|
return new Ipb4Extension(settings, columns);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
package fr.xephi.authme.datasource.mysqlextensions;
|
||||||
|
|
||||||
|
import fr.xephi.authme.TestHelper;
|
||||||
|
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||||
|
import fr.xephi.authme.datasource.Columns;
|
||||||
|
import fr.xephi.authme.security.crypts.HashedPassword;
|
||||||
|
import fr.xephi.authme.settings.Settings;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for {@link NoOpExtension}.
|
||||||
|
*/
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class NoOpExtensionTest {
|
||||||
|
|
||||||
|
private NoOpExtension extension;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void createExtension() {
|
||||||
|
Settings settings = mock(Settings.class);
|
||||||
|
TestHelper.returnDefaultsForAllProperties(settings);
|
||||||
|
Columns columns = new Columns(settings);
|
||||||
|
extension = new NoOpExtension(settings, columns);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldNotHaveAnyInteractionsWithConnection() throws SQLException {
|
||||||
|
// given
|
||||||
|
Connection connection = mock(Connection.class);
|
||||||
|
PlayerAuth auth = mock(PlayerAuth.class);
|
||||||
|
int id = 3;
|
||||||
|
String name = "Bobby";
|
||||||
|
HashedPassword password = new HashedPassword("test", "toast");
|
||||||
|
|
||||||
|
|
||||||
|
// when
|
||||||
|
extension.extendAuth(auth, id, connection);
|
||||||
|
extension.changePassword(name, password, connection);
|
||||||
|
extension.removeAuth(name, connection);
|
||||||
|
extension.saveAuth(auth, connection);
|
||||||
|
|
||||||
|
// then
|
||||||
|
verifyZeroInteractions(connection, auth);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
package fr.xephi.authme.datasource.mysqlextensions;
|
||||||
|
|
||||||
|
import fr.xephi.authme.datasource.Columns;
|
||||||
|
import fr.xephi.authme.settings.Settings;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resource closing test for {@link PhpBbExtension}.
|
||||||
|
*/
|
||||||
|
public class PhpBbExtensionResourceClosingTest extends AbstractMySqlExtensionResourceClosingTest {
|
||||||
|
|
||||||
|
public PhpBbExtensionResourceClosingTest(Method method, String name) {
|
||||||
|
super(method, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected MySqlExtension createExtension(Settings settings, Columns columns) {
|
||||||
|
return new PhpBbExtension(settings, columns);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
package fr.xephi.authme.datasource.mysqlextensions;
|
||||||
|
|
||||||
|
import fr.xephi.authme.datasource.Columns;
|
||||||
|
import fr.xephi.authme.settings.Settings;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resource closing test for {@link WordpressExtension}.
|
||||||
|
*/
|
||||||
|
public class WordpressExtensionResourceClosingTest extends AbstractMySqlExtensionResourceClosingTest {
|
||||||
|
|
||||||
|
public WordpressExtensionResourceClosingTest(Method method, String name) {
|
||||||
|
super(method, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected MySqlExtension createExtension(Settings settings, Columns columns) {
|
||||||
|
return new WordpressExtension(settings, columns);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
package fr.xephi.authme.datasource.mysqlextensions;
|
||||||
|
|
||||||
|
import fr.xephi.authme.datasource.Columns;
|
||||||
|
import fr.xephi.authme.settings.Settings;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resource closing test for {@link XfBcryptExtension}.
|
||||||
|
*/
|
||||||
|
public class XfBcryptExtensionResourceClosingTest extends AbstractMySqlExtensionResourceClosingTest {
|
||||||
|
|
||||||
|
public XfBcryptExtensionResourceClosingTest(Method method, String name) {
|
||||||
|
super(method, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected MySqlExtension createExtension(Settings settings, Columns columns) {
|
||||||
|
return new XfBcryptExtension(settings, columns);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,9 +1,9 @@
|
|||||||
package tools.docs.hashmethods;
|
package tools.docs.hashmethods;
|
||||||
|
|
||||||
import ch.jalu.configme.properties.Property;
|
|
||||||
import ch.jalu.injector.Injector;
|
import ch.jalu.injector.Injector;
|
||||||
import ch.jalu.injector.InjectorBuilder;
|
import ch.jalu.injector.InjectorBuilder;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import fr.xephi.authme.TestHelper;
|
||||||
import fr.xephi.authme.security.HashAlgorithm;
|
import fr.xephi.authme.security.HashAlgorithm;
|
||||||
import fr.xephi.authme.security.crypts.EncryptionMethod;
|
import fr.xephi.authme.security.crypts.EncryptionMethod;
|
||||||
import fr.xephi.authme.security.crypts.HexSaltedMethod;
|
import fr.xephi.authme.security.crypts.HexSaltedMethod;
|
||||||
@ -11,8 +11,6 @@ import fr.xephi.authme.security.crypts.description.AsciiRestricted;
|
|||||||
import fr.xephi.authme.security.crypts.description.HasSalt;
|
import fr.xephi.authme.security.crypts.description.HasSalt;
|
||||||
import fr.xephi.authme.security.crypts.description.Recommendation;
|
import fr.xephi.authme.security.crypts.description.Recommendation;
|
||||||
import fr.xephi.authme.settings.Settings;
|
import fr.xephi.authme.settings.Settings;
|
||||||
import org.mockito.invocation.InvocationOnMock;
|
|
||||||
import org.mockito.stubbing.Answer;
|
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -20,9 +18,7 @@ import java.util.LinkedHashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gathers information on {@link EncryptionMethod} implementations based on
|
* Gathers information on {@link EncryptionMethod} implementations based on
|
||||||
@ -140,17 +136,9 @@ public class EncryptionMethodInfoGatherer {
|
|||||||
return key.cast(map.get(key));
|
return key.cast(map.get(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private static Injector createInitializer() {
|
private static Injector createInitializer() {
|
||||||
Settings settings = mock(Settings.class);
|
Settings settings = mock(Settings.class);
|
||||||
// Return the default value for any property
|
TestHelper.returnDefaultsForAllProperties(settings);
|
||||||
when(settings.getProperty(any(Property.class))).thenAnswer(new Answer<Object>() {
|
|
||||||
@Override
|
|
||||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
|
||||||
Property<?> property = (Property<?>) invocation.getArguments()[0];
|
|
||||||
return property.getDefaultValue();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// By passing some bogus "package" to the constructor, the injector will throw if it needs to
|
// By passing some bogus "package" to the constructor, the injector will throw if it needs to
|
||||||
// instantiate any dependency other than what we provide.
|
// instantiate any dependency other than what we provide.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user