#1255 Extract forum-specific data source actions into separate extension classes

This commit is contained in:
ljacqu 2017-07-23 14:01:35 +02:00
parent 870810a230
commit cbc794ba20
14 changed files with 589 additions and 420 deletions

View File

@ -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,9 +76,9 @@ 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);
} }
/** /**
@ -96,7 +86,7 @@ public class MySQL implements DataSource {
* *
* @param settings the settings to read properties from * @param settings the settings to read properties from
*/ */
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 +95,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 +286,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 +333,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 +370,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 +422,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;
@ -922,26 +625,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 +676,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.
* *

View File

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

View File

@ -0,0 +1,68 @@
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.DatabaseSettings;
import fr.xephi.authme.settings.properties.HooksSettings;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* Extension for IPB4.
*/
class Ipb4Extension extends MySqlExtension {
private final Columns col;
private final String tableName;
private final String ipbPrefix;
private final int ipbGroup;
Ipb4Extension(Settings settings, Columns col) {
this.col = col;
this.tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
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 {
String 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();
}
}
}
}
}
}

View File

@ -0,0 +1,62 @@
package fr.xephi.authme.datasource.mysqlextensions;
import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.security.crypts.HashedPassword;
import java.sql.Connection;
import java.sql.SQLException;
/**
* 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 {
/**
* 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
}
}

View File

@ -0,0 +1,33 @@
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;
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();
}
}
}

View File

@ -0,0 +1,7 @@
package fr.xephi.authme.datasource.mysqlextensions;
/**
* Extension implementation that does not do anything.
*/
class NoOpExtension extends MySqlExtension {
}

View File

@ -0,0 +1,93 @@
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.DatabaseSettings;
import fr.xephi.authme.settings.properties.HooksSettings;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* Extensions for phpBB when MySQL is used as data source.
*/
class PhpBbExtension extends MySqlExtension {
private final Columns col;
private final String tableName;
private final String phpBbPrefix;
private final int phpBbGroup;
PhpBbExtension(Settings settings, Columns col) {
this.col = col;
this.tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
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 {
String 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);
updateSpecificsOnSave(id, 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();
}
}
}

View File

@ -0,0 +1,107 @@
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.DatabaseSettings;
import fr.xephi.authme.settings.properties.HooksSettings;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* MySQL extensions for Wordpress.
*/
class WordpressExtension extends MySqlExtension {
private final Columns col;
private final String tableName;
private final String wordpressPrefix;
WordpressExtension(Settings settings, Columns col) {
this.col = col;
this.tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
this.wordpressPrefix = settings.getProperty(HooksSettings.WORDPRESS_TABLE_PREFIX);
}
@Override
public void saveAuth(PlayerAuth auth, Connection con) throws SQLException {
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);
String 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
pst2.executeBatch();
pst2.clearBatch();
}
}
}
}
}
}

View File

@ -0,0 +1,160 @@
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.DatabaseSettings;
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;
/**
* Extension for XFBCRYPT.
*/
class XfBcryptExtension extends MySqlExtension {
private final Columns col;
private final String tableName;
private final String xfPrefix;
private final int xfGroup;
XfBcryptExtension(Settings settings, Columns col) {
this.col = col;
this.tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
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 {
String 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);
updateXenforoTablesOnSave(auth, id, 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 {
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
// TODO #1255: Close these statements with try-catch
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();
}
}
}
}
@Override
public void removeAuth(String user, Connection con) throws SQLException {
String 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();
}
}
}
}
}
}

View File

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

View File

@ -1,16 +1,13 @@
package fr.xephi.authme.datasource; package fr.xephi.authme.datasource;
import ch.jalu.configme.properties.Property; 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 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.Settings;
import fr.xephi.authme.settings.properties.SecuritySettings;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
@ -19,7 +16,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;
@ -64,13 +60,6 @@ public abstract class AbstractResourceClosingTest {
/** 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. */ /** Mock of a settings instance. */
private static Settings settings; private static Settings settings;
@ -84,23 +73,21 @@ public abstract class AbstractResourceClosingTest {
private List<AutoCloseable> closeables = new ArrayList<>(); private List<AutoCloseable> closeables = new ArrayList<>();
/** /**
* 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. */ /** Initialize the settings mock and makes it return the default of any given property by default. */
@SuppressWarnings({ "unchecked", "rawtypes" }) @SuppressWarnings("unchecked")
@BeforeClass @BeforeClass
public static void initializeSettings() throws IOException, ClassNotFoundException { public static void initializeSettings() {
settings = mock(Settings.class); settings = mock(Settings.class);
given(settings.getProperty(any(Property.class))).willAnswer(new Answer() { given(settings.getProperty(any(Property.class))).willAnswer(new Answer() {
@Override @Override
@ -129,24 +116,16 @@ public abstract class AbstractResourceClosingTest {
} }
/** /**
* Initialization method -- provides the parameters to run the test with by scanning all DataSource * Initialization method -- provides the parameters to run the test with by scanning all DataSource methods.
* 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 * @return Test parameters
*/ */
@Parameterized.Parameters(name = "{1}({2})") @Parameterized.Parameters(name = "{1}")
public static Collection<Object[]> data() { public static Collection<Object[]> data() {
List<Method> methods = getDataSourceMethods(); List<Method> methods = getDataSourceMethods();
List<Object[]> data = new ArrayList<>(); 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) { for (Method method : methods) {
HashAlgorithm[] algorithms = MoreObjects.firstNonNull(CUSTOM_ALGORITHMS.get(method.getName()), defaultAlgorithm); data.add(new Object[]{method, method.getName()});
for (HashAlgorithm algorithm : algorithms) {
data.add(new Object[]{method, method.getName(), algorithm});
}
} }
return data; return data;
} }
@ -247,20 +226,6 @@ 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
// --------------------- // ---------------------

View File

@ -4,6 +4,8 @@ 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;
@ -30,6 +32,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. */
@ -51,6 +55,8 @@ public class MySqlIntegrationTest extends AbstractDataSourceIntegrationTest {
return ((Property) invocation.getArguments()[0]).getDefaultValue(); return ((Property) invocation.getArguments()[0]).getDefaultValue();
} }
}); });
extensionsFactory = mock(MySqlExtensionsFactory.class);
when(extensionsFactory.buildExtension(any(Columns.class))).thenReturn(mock(MySqlExtension.class));
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 +91,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) {

View File

@ -1,12 +1,14 @@
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;
@ -15,15 +17,17 @@ import static org.mockito.Mockito.mock;
*/ */
public class MySqlResourceClosingTest extends AbstractResourceClosingTest { public class MySqlResourceClosingTest extends AbstractResourceClosingTest {
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);
} }
} }

View File

@ -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;
@ -11,8 +10,8 @@ import java.sql.Connection;
*/ */
public class SQLiteResourceClosingTest extends AbstractResourceClosingTest { public class SQLiteResourceClosingTest extends AbstractResourceClosingTest {
public SQLiteResourceClosingTest(Method method, String name, HashAlgorithm algorithm) { public SQLiteResourceClosingTest(Method method, String name) {
super(method, name, algorithm); super(method, name);
} }
@Override @Override