diff --git a/LaunchServer/LaunchServer.iml b/LaunchServer/LaunchServer.iml index f54725d..d63758e 100644 --- a/LaunchServer/LaunchServer.iml +++ b/LaunchServer/LaunchServer.iml @@ -36,8 +36,24 @@ - - + + + + + + + + + + + + + + + + + + @@ -78,5 +94,7 @@ + + \ No newline at end of file diff --git a/LaunchServer/source/auth/MariaDBSourceConfig.java b/LaunchServer/source/auth/MariaDBSourceConfig.java new file mode 100644 index 0000000..f63f347 --- /dev/null +++ b/LaunchServer/source/auth/MariaDBSourceConfig.java @@ -0,0 +1,129 @@ +package launchserver.auth; + +import org.mariadb.jdbc.MariaDbDataSource; +import com.zaxxer.hikari.HikariDataSource; +import launcher.LauncherAPI; +import launcher.helper.LogHelper; +import launcher.helper.VerifyHelper; +import launcher.serialize.config.ConfigObject; +import launcher.serialize.config.entry.BlockConfigEntry; +import launcher.serialize.config.entry.IntegerConfigEntry; +import launcher.serialize.config.entry.StringConfigEntry; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; + +public final class MariaDBSourceConfig extends ConfigObject implements AutoCloseable, SQLSourceConfig +{ + @LauncherAPI + public static final int TIMEOUT = VerifyHelper.verifyInt( + Integer.parseInt(System.getProperty("launcher.mysql.idleTimeout", Integer.toString(5000))), + VerifyHelper.POSITIVE, "launcher.mysql.idleTimeout can't be <= 5000"); + private static final int MAX_POOL_SIZE = VerifyHelper.verifyInt( + Integer.parseInt(System.getProperty("launcher.mysql.maxPoolSize", Integer.toString(3))), + VerifyHelper.POSITIVE, "launcher.mysql.maxPoolSize can't be <= 0"); + + // Instance + private final String poolName; + + // Config + private final String address; + private final int port; + private final String username; + private final String password; + private final String database; + + // Cache + private DataSource source; + private boolean hikari; + + @LauncherAPI + public MariaDBSourceConfig(String poolName, BlockConfigEntry block) + { + super(block); + this.poolName = poolName; + address = VerifyHelper.verify(block.getEntryValue("address", StringConfigEntry.class), + VerifyHelper.NOT_EMPTY, "MySQL address can't be empty"); + port = VerifyHelper.verifyInt(block.getEntryValue("port", IntegerConfigEntry.class), + VerifyHelper.range(0, 65535), "Illegal MySQL port"); + username = VerifyHelper.verify(block.getEntryValue("username", StringConfigEntry.class), + VerifyHelper.NOT_EMPTY, "MySQL username can't be empty"); + password = block.getEntryValue("password", StringConfigEntry.class); + database = VerifyHelper.verify(block.getEntryValue("database", StringConfigEntry.class), + VerifyHelper.NOT_EMPTY, "MySQL database can't be empty"); + + // Password shouldn't be verified + } + + @Override + public synchronized void close() + { + if (hikari) + { // Shutdown hikari pool + ((HikariDataSource) source).close(); + } + } + + @LauncherAPI + public synchronized Connection getConnection() throws SQLException + { + if (source == null) + { // New data source + MariaDbDataSource mariaDbSource = new MariaDbDataSource(); + + // Нету такого функционала у конектора MariaDB, попробуем так + //mariaDbSource.setCharacterEncoding("UTF-8"); + //mariaDbSource.setUseSSL(false); + + // Prep statements cache + //mariaDbSource.setPrepStmtCacheSize(250); + //mariaDbSource.setPrepStmtCacheSqlLimit(2048); + //mariaDbSource.setCachePrepStmts(true); + //mariaDbSource.setUseServerPrepStmts(true); + + // General optimizations + //mariaDbSource.setCacheServerConfiguration(true); + //mariaDbSource.setUseLocalSessionState(true); + //mariaDbSource.setRewriteBatchedStatements(true); + //mariaDbSource.setMaintainTimeStats(false); + //mariaDbSource.setUseUnbufferedInput(false); + //mariaDbSource.setUseReadAheadInput(false); + //mariaDbSource.setTcpNoDelay(true); + + // Set credentials + mariaDbSource.setServerName(address); + mariaDbSource.setPortNumber(port); + mariaDbSource.setUser(username); + mariaDbSource.setPassword(password); + mariaDbSource.setDatabaseName(database); + + // Try using HikariCP + source = mariaDbSource; + try + { + Class.forName("com.zaxxer.hikari.HikariDataSource"); + hikari = true; // Used for shutdown. Not instanceof because of possible classpath error + + // Set HikariCP pool + HikariDataSource hikariSource = new HikariDataSource(); + hikariSource.setDataSource(source); + + // Set pool settings + hikariSource.setPoolName(poolName); + hikariSource.setMinimumIdle(0); + hikariSource.setMaximumPoolSize(MAX_POOL_SIZE); + hikariSource.setIdleTimeout(TIMEOUT * 1000L); + + // Replace source with hds + source = hikariSource; + LogHelper.info("HikariCP pooling enabled for '%s'", poolName); + } + catch (ClassNotFoundException ignored) + { + LogHelper.warning("HikariCP isn't in classpath for '%s'", poolName); + } + } + return source.getConnection(); + } +} diff --git a/LaunchServer/source/auth/MySQL8SourceConfig.java b/LaunchServer/source/auth/MySQL8SourceConfig.java new file mode 100644 index 0000000..2d830f6 --- /dev/null +++ b/LaunchServer/source/auth/MySQL8SourceConfig.java @@ -0,0 +1,127 @@ +package launchserver.auth; + +import com.mysql.cj.jdbc.MysqlDataSource; +import com.zaxxer.hikari.HikariDataSource; +import launcher.LauncherAPI; +import launcher.helper.LogHelper; +import launcher.helper.VerifyHelper; +import launcher.serialize.config.ConfigObject; +import launcher.serialize.config.entry.BlockConfigEntry; +import launcher.serialize.config.entry.IntegerConfigEntry; +import launcher.serialize.config.entry.StringConfigEntry; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; + +public final class MySQL8SourceConfig extends ConfigObject implements AutoCloseable, SQLSourceConfig +{ + @LauncherAPI + public static final int TIMEOUT = VerifyHelper.verifyInt( + Integer.parseInt(System.getProperty("launcher.mysql.idleTimeout", Integer.toString(5000))), + VerifyHelper.POSITIVE, "launcher.mysql.idleTimeout can't be <= 5000"); + private static final int MAX_POOL_SIZE = VerifyHelper.verifyInt( + Integer.parseInt(System.getProperty("launcher.mysql.maxPoolSize", Integer.toString(3))), + VerifyHelper.POSITIVE, "launcher.mysql.maxPoolSize can't be <= 0"); + + // Instance + private final String poolName; + + // Config + private final String address; + private final int port; + private final String username; + private final String password; + private final String database; + + // Cache + private DataSource source; + private boolean hikari; + + @LauncherAPI + public MySQL8SourceConfig(String poolName, BlockConfigEntry block) + { + super(block); + this.poolName = poolName; + address = VerifyHelper.verify(block.getEntryValue("address", StringConfigEntry.class), + VerifyHelper.NOT_EMPTY, "MySQL address can't be empty"); + port = VerifyHelper.verifyInt(block.getEntryValue("port", IntegerConfigEntry.class), + VerifyHelper.range(0, 65535), "Illegal MySQL port"); + username = VerifyHelper.verify(block.getEntryValue("username", StringConfigEntry.class), + VerifyHelper.NOT_EMPTY, "MySQL username can't be empty"); + password = block.getEntryValue("password", StringConfigEntry.class); + database = VerifyHelper.verify(block.getEntryValue("database", StringConfigEntry.class), + VerifyHelper.NOT_EMPTY, "MySQL database can't be empty"); + + // Password shouldn't be verified + } + + @Override + public synchronized void close() + { + if (hikari) + { // Shutdown hikari pool + ((HikariDataSource) source).close(); + } + } + + @LauncherAPI + public synchronized Connection getConnection() throws SQLException + { + if (source == null) + { // New data source + MysqlDataSource mysqlSource = new MysqlDataSource(); + mysqlSource.setCharacterEncoding("UTF-8"); + mysqlSource.setUseSSL(false); + + // Prep statements cache + mysqlSource.setPrepStmtCacheSize(250); + mysqlSource.setPrepStmtCacheSqlLimit(2048); + mysqlSource.setCachePrepStmts(true); + mysqlSource.setUseServerPrepStmts(true); + + // General optimizations + mysqlSource.setCacheServerConfiguration(true); + mysqlSource.setUseLocalSessionState(true); + mysqlSource.setRewriteBatchedStatements(true); + mysqlSource.setMaintainTimeStats(false); + mysqlSource.setUseUnbufferedInput(false); + mysqlSource.setUseReadAheadInput(false); + mysqlSource.setTcpNoDelay(true); + + // Set credentials + mysqlSource.setServerName(address); + mysqlSource.setPortNumber(port); + mysqlSource.setUser(username); + mysqlSource.setPassword(password); + mysqlSource.setDatabaseName(database); + + // Try using HikariCP + source = mysqlSource; + try + { + Class.forName("com.zaxxer.hikari.HikariDataSource"); + hikari = true; // Used for shutdown. Not instanceof because of possible classpath error + + // Set HikariCP pool + HikariDataSource hikariSource = new HikariDataSource(); + hikariSource.setDataSource(source); + + // Set pool settings + hikariSource.setPoolName(poolName); + hikariSource.setMinimumIdle(0); + hikariSource.setMaximumPoolSize(MAX_POOL_SIZE); + hikariSource.setIdleTimeout(TIMEOUT * 1000L); + + // Replace source with hds + source = hikariSource; + LogHelper.info("HikariCP pooling enabled for '%s'", poolName); + } + catch (ClassNotFoundException ignored) + { + LogHelper.warning("HikariCP isn't in classpath for '%s'", poolName); + } + } + return source.getConnection(); + } +} diff --git a/LaunchServer/source/auth/handler/AuthHandler.java b/LaunchServer/source/auth/handler/AuthHandler.java index f578748..e1de616 100644 --- a/LaunchServer/source/auth/handler/AuthHandler.java +++ b/LaunchServer/source/auth/handler/AuthHandler.java @@ -26,6 +26,8 @@ registerHandler("binaryFile", BinaryFileAuthHandler::new); registerHandler("textFile", TextFileAuthHandler::new); registerHandler("mysql", MySQLAuthHandler::new); + registerHandler("mysql-8", MySQL8AuthHandler::new); + registerHandler("mariadb", MariaDBAuthHandler::new); registerHandler("postgresql", PostgreSQLAuthHandler::new); registerHandler("json", JsonAuthHandler::new); } diff --git a/LaunchServer/source/auth/handler/MariaDBAuthHandler.java b/LaunchServer/source/auth/handler/MariaDBAuthHandler.java new file mode 100644 index 0000000..9a33ed1 --- /dev/null +++ b/LaunchServer/source/auth/handler/MariaDBAuthHandler.java @@ -0,0 +1,155 @@ +package launchserver.auth.handler; + +import launcher.helper.LogHelper; +import launcher.helper.VerifyHelper; +import launcher.serialize.config.entry.BlockConfigEntry; +import launcher.serialize.config.entry.BooleanConfigEntry; +import launcher.serialize.config.entry.StringConfigEntry; +import launchserver.auth.MariaDBSourceConfig; + +import java.io.IOException; +import java.sql.*; +import java.util.UUID; + +public final class MariaDBAuthHandler extends CachedAuthHandler +{ + private final MariaDBSourceConfig mariaDBHolder; + private final String uuidColumn; + private final String usernameColumn; + private final String accessTokenColumn; + private final String serverIDColumn; + + // Prepared SQL queries + private final String queryByUUIDSQL; + private final String queryByUsernameSQL; + private final String updateAuthSQL; + private final String updateServerIDSQL; + + MariaDBAuthHandler(BlockConfigEntry block) + { + super(block); + mariaDBHolder = new MariaDBSourceConfig("authHandlerPool", block); + + // Read query params + String table = VerifyHelper.verifyIDName( + block.getEntryValue("table", StringConfigEntry.class)); + uuidColumn = VerifyHelper.verifyIDName( + block.getEntryValue("uuidColumn", StringConfigEntry.class)); + usernameColumn = VerifyHelper.verifyIDName( + block.getEntryValue("usernameColumn", StringConfigEntry.class)); + accessTokenColumn = VerifyHelper.verifyIDName( + block.getEntryValue("accessTokenColumn", StringConfigEntry.class)); + serverIDColumn = VerifyHelper.verifyIDName( + block.getEntryValue("serverIDColumn", StringConfigEntry.class)); + + // Prepare SQL queries + queryByUUIDSQL = String.format("SELECT %s, %s, %s, %s FROM %s WHERE %s=? LIMIT 1", + uuidColumn, usernameColumn, accessTokenColumn, serverIDColumn, table, uuidColumn); + queryByUsernameSQL = String.format("SELECT %s, %s, %s, %s FROM %s WHERE %s=? LIMIT 1", + uuidColumn, usernameColumn, accessTokenColumn, serverIDColumn, table, usernameColumn); + updateAuthSQL = String.format("UPDATE %s SET %s=?, %s=?, %s=NULL WHERE %s=? LIMIT 1", + table, usernameColumn, accessTokenColumn, serverIDColumn, uuidColumn); + updateServerIDSQL = String.format("UPDATE %s SET %s=? WHERE %s=? LIMIT 1", + table, serverIDColumn, uuidColumn); + + // Fetch all entries + if (block.getEntryValue("fetchAll", BooleanConfigEntry.class)) + { + LogHelper.info("Fetching all AuthHandler entries"); + String query = String.format("SELECT %s, %s, %s, %s FROM %s", + uuidColumn, usernameColumn, accessTokenColumn, serverIDColumn, table); + try (Connection c = mariaDBHolder.getConnection(); Statement statement = c.createStatement(); + ResultSet set = statement.executeQuery(query)) + { + for (Entry entry = constructEntry(set); entry != null; entry = constructEntry(set)) + { + addEntry(entry); + } + } + catch (SQLException e) + { + LogHelper.error(e); + } + } + } + + @Override + public void close() + { + mariaDBHolder.close(); + } + + @Override + protected Entry fetchEntry(String username) throws IOException + { + return query(queryByUsernameSQL, username); + } + + @Override + protected Entry fetchEntry(UUID uuid) throws IOException + { + return query(queryByUUIDSQL, uuid.toString()); + } + + @Override + protected boolean updateAuth(UUID uuid, String username, String accessToken) throws IOException + { + try (Connection c = mariaDBHolder.getConnection(); PreparedStatement s = c.prepareStatement(updateAuthSQL)) + { + s.setString(1, username); // Username case + s.setString(2, accessToken); + s.setString(3, uuid.toString()); + + // Execute update + s.setQueryTimeout(MariaDBSourceConfig.TIMEOUT); + return s.executeUpdate() > 0; + } + catch (SQLException e) + { + throw new IOException(e); + } + } + + @Override + protected boolean updateServerID(UUID uuid, String serverID) throws IOException + { + try (Connection c = mariaDBHolder.getConnection(); PreparedStatement s = c.prepareStatement(updateServerIDSQL)) + { + s.setString(1, serverID); + s.setString(2, uuid.toString()); + + // Execute update + s.setQueryTimeout(MariaDBSourceConfig.TIMEOUT); + return s.executeUpdate() > 0; + } + catch (SQLException e) + { + throw new IOException(e); + } + } + + private Entry constructEntry(ResultSet set) throws SQLException + { + return set.next() ? new Entry(UUID.fromString(set.getString(uuidColumn)), set.getString(usernameColumn), + set.getString(accessTokenColumn), set.getString(serverIDColumn)) : null; + } + + private Entry query(String sql, String value) throws IOException + { + try (Connection c = mariaDBHolder.getConnection(); PreparedStatement s = c.prepareStatement(sql)) + { + s.setString(1, value); + + // Execute query + s.setQueryTimeout(MariaDBSourceConfig.TIMEOUT); + try (ResultSet set = s.executeQuery()) + { + return constructEntry(set); + } + } + catch (SQLException e) + { + throw new IOException(e); + } + } +} diff --git a/LaunchServer/source/auth/handler/MySQL8AuthHandler.java b/LaunchServer/source/auth/handler/MySQL8AuthHandler.java new file mode 100644 index 0000000..4a376c7 --- /dev/null +++ b/LaunchServer/source/auth/handler/MySQL8AuthHandler.java @@ -0,0 +1,155 @@ +package launchserver.auth.handler; + +import launcher.helper.LogHelper; +import launcher.helper.VerifyHelper; +import launcher.serialize.config.entry.BlockConfigEntry; +import launcher.serialize.config.entry.BooleanConfigEntry; +import launcher.serialize.config.entry.StringConfigEntry; +import launchserver.auth.MySQL8SourceConfig; + +import java.io.IOException; +import java.sql.*; +import java.util.UUID; + +public final class MySQL8AuthHandler extends CachedAuthHandler +{ + private final MySQL8SourceConfig mySQL8Holder; + private final String uuidColumn; + private final String usernameColumn; + private final String accessTokenColumn; + private final String serverIDColumn; + + // Prepared SQL queries + private final String queryByUUIDSQL; + private final String queryByUsernameSQL; + private final String updateAuthSQL; + private final String updateServerIDSQL; + + MySQL8AuthHandler(BlockConfigEntry block) + { + super(block); + mySQL8Holder = new MySQL8SourceConfig("authHandlerPool", block); + + // Read query params + String table = VerifyHelper.verifyIDName( + block.getEntryValue("table", StringConfigEntry.class)); + uuidColumn = VerifyHelper.verifyIDName( + block.getEntryValue("uuidColumn", StringConfigEntry.class)); + usernameColumn = VerifyHelper.verifyIDName( + block.getEntryValue("usernameColumn", StringConfigEntry.class)); + accessTokenColumn = VerifyHelper.verifyIDName( + block.getEntryValue("accessTokenColumn", StringConfigEntry.class)); + serverIDColumn = VerifyHelper.verifyIDName( + block.getEntryValue("serverIDColumn", StringConfigEntry.class)); + + // Prepare SQL queries + queryByUUIDSQL = String.format("SELECT %s, %s, %s, %s FROM %s WHERE %s=? LIMIT 1", + uuidColumn, usernameColumn, accessTokenColumn, serverIDColumn, table, uuidColumn); + queryByUsernameSQL = String.format("SELECT %s, %s, %s, %s FROM %s WHERE %s=? LIMIT 1", + uuidColumn, usernameColumn, accessTokenColumn, serverIDColumn, table, usernameColumn); + updateAuthSQL = String.format("UPDATE %s SET %s=?, %s=?, %s=NULL WHERE %s=? LIMIT 1", + table, usernameColumn, accessTokenColumn, serverIDColumn, uuidColumn); + updateServerIDSQL = String.format("UPDATE %s SET %s=? WHERE %s=? LIMIT 1", + table, serverIDColumn, uuidColumn); + + // Fetch all entries + if (block.getEntryValue("fetchAll", BooleanConfigEntry.class)) + { + LogHelper.info("Fetching all AuthHandler entries"); + String query = String.format("SELECT %s, %s, %s, %s FROM %s", + uuidColumn, usernameColumn, accessTokenColumn, serverIDColumn, table); + try (Connection c = mySQL8Holder.getConnection(); Statement statement = c.createStatement(); + ResultSet set = statement.executeQuery(query)) + { + for (Entry entry = constructEntry(set); entry != null; entry = constructEntry(set)) + { + addEntry(entry); + } + } + catch (SQLException e) + { + LogHelper.error(e); + } + } + } + + @Override + public void close() + { + mySQL8Holder.close(); + } + + @Override + protected Entry fetchEntry(String username) throws IOException + { + return query(queryByUsernameSQL, username); + } + + @Override + protected Entry fetchEntry(UUID uuid) throws IOException + { + return query(queryByUUIDSQL, uuid.toString()); + } + + @Override + protected boolean updateAuth(UUID uuid, String username, String accessToken) throws IOException + { + try (Connection c = mySQL8Holder.getConnection(); PreparedStatement s = c.prepareStatement(updateAuthSQL)) + { + s.setString(1, username); // Username case + s.setString(2, accessToken); + s.setString(3, uuid.toString()); + + // Execute update + s.setQueryTimeout(MySQL8SourceConfig.TIMEOUT); + return s.executeUpdate() > 0; + } + catch (SQLException e) + { + throw new IOException(e); + } + } + + @Override + protected boolean updateServerID(UUID uuid, String serverID) throws IOException + { + try (Connection c = mySQL8Holder.getConnection(); PreparedStatement s = c.prepareStatement(updateServerIDSQL)) + { + s.setString(1, serverID); + s.setString(2, uuid.toString()); + + // Execute update + s.setQueryTimeout(MySQL8SourceConfig.TIMEOUT); + return s.executeUpdate() > 0; + } + catch (SQLException e) + { + throw new IOException(e); + } + } + + private Entry constructEntry(ResultSet set) throws SQLException + { + return set.next() ? new Entry(UUID.fromString(set.getString(uuidColumn)), set.getString(usernameColumn), + set.getString(accessTokenColumn), set.getString(serverIDColumn)) : null; + } + + private Entry query(String sql, String value) throws IOException + { + try (Connection c = mySQL8Holder.getConnection(); PreparedStatement s = c.prepareStatement(sql)) + { + s.setString(1, value); + + // Execute query + s.setQueryTimeout(MySQL8SourceConfig.TIMEOUT); + try (ResultSet set = s.executeQuery()) + { + return constructEntry(set); + } + } + catch (SQLException e) + { + throw new IOException(e); + } + } +} diff --git a/LaunchServer/source/auth/provider/AuthProvider.java b/LaunchServer/source/auth/provider/AuthProvider.java index d51023f..89c0ea9 100644 --- a/LaunchServer/source/auth/provider/AuthProvider.java +++ b/LaunchServer/source/auth/provider/AuthProvider.java @@ -26,6 +26,10 @@ registerProvider("mojang", MojangAuthProvider::new); registerProvider("mysql", MySQLAuthProvider::new); registerProvider("mysql-bcrypt", MySQLBcryptAuthProvider::new); + registerProvider("mysql-8", MySQL8AuthProvider::new); + registerProvider("mysql-8-bcrypt", MySQL8BcryptAuthProvider::new); + registerProvider("mariadb", MariaDBAuthProvider::new); + registerProvider("mariadb-bcrypt", MariaDBBcryptAuthProvider::new); registerProvider("request", RequestAuthProvider::new); registerProvider("postgresql", PostgreSQLAuthProvider::new); registerProvider("json", JsonAuthProvider::new); diff --git a/LaunchServer/source/auth/provider/MariaDBAuthProvider.java b/LaunchServer/source/auth/provider/MariaDBAuthProvider.java new file mode 100644 index 0000000..f74b5bf --- /dev/null +++ b/LaunchServer/source/auth/provider/MariaDBAuthProvider.java @@ -0,0 +1,60 @@ +package launchserver.auth.provider; + +import launcher.helper.CommonHelper; +import launcher.helper.SecurityHelper; +import launcher.helper.VerifyHelper; +import launcher.serialize.config.entry.BlockConfigEntry; +import launcher.serialize.config.entry.ListConfigEntry; +import launcher.serialize.config.entry.StringConfigEntry; +import launchserver.auth.AuthException; +import launchserver.auth.MariaDBSourceConfig; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +public final class MariaDBAuthProvider extends AuthProvider +{ + private final MariaDBSourceConfig mySQLHolder; + private final String query; + private final String[] queryParams; + + MariaDBAuthProvider(BlockConfigEntry block) + { + super(block); + mySQLHolder = new MariaDBSourceConfig("authProviderPool", block); + + // Read query + query = VerifyHelper.verify(block.getEntryValue("query", StringConfigEntry.class), + VerifyHelper.NOT_EMPTY, "MySQL query can't be empty"); + queryParams = block.getEntry("queryParams", ListConfigEntry.class). + stream(StringConfigEntry.class).toArray(String[]::new); + } + + @Override + public AuthProviderResult auth(String login, String password, String ip) throws SQLException, AuthException + { + try (Connection c = mySQLHolder.getConnection(); PreparedStatement s = c.prepareStatement(query)) + { + String[] replaceParams = {"login", login, "password", password, "ip", ip}; + for (int i = 0; i < queryParams.length; i++) + { + s.setString(i + 1, CommonHelper.replace(queryParams[i], replaceParams)); + } + + // Execute SQL query + s.setQueryTimeout(MariaDBSourceConfig.TIMEOUT); + try (ResultSet set = s.executeQuery()) + { + return set.next() ? new AuthProviderResult(set.getString(1), SecurityHelper.randomStringToken()) : authError("Incorrect username or password"); + } + } + } + + @Override + public void close() + { + // Do nothing + } +} diff --git a/LaunchServer/source/auth/provider/MariaDBBcryptAuthProvider.java b/LaunchServer/source/auth/provider/MariaDBBcryptAuthProvider.java new file mode 100644 index 0000000..5185e64 --- /dev/null +++ b/LaunchServer/source/auth/provider/MariaDBBcryptAuthProvider.java @@ -0,0 +1,60 @@ +package launchserver.auth.provider; + +import launcher.helper.CommonHelper; +import launcher.helper.SecurityHelper; +import launcher.helper.VerifyHelper; +import launcher.serialize.config.entry.BlockConfigEntry; +import launcher.serialize.config.entry.ListConfigEntry; +import launcher.serialize.config.entry.StringConfigEntry; +import launchserver.auth.AuthException; +import launchserver.auth.MariaDBSourceConfig; +import org.mindrot.jbcrypt.BCrypt; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +public final class MariaDBBcryptAuthProvider extends AuthProvider +{ + private final MariaDBSourceConfig mySQLHolder; + private final String query; + private final String[] queryParams; + + MariaDBBcryptAuthProvider(BlockConfigEntry block) + { + super(block); + mySQLHolder = new MariaDBSourceConfig("authProviderPool", block); + + query = VerifyHelper.verify(block.getEntryValue("query", StringConfigEntry.class), + VerifyHelper.NOT_EMPTY, "MySQL query can't be empty"); + queryParams = block.getEntry("queryParams", ListConfigEntry.class). + stream(StringConfigEntry.class).toArray(String[]::new); + } + + @Override + public AuthProviderResult auth(String login, String password, String ip) throws SQLException, AuthException + { + try (Connection c = mySQLHolder.getConnection(); PreparedStatement s = c.prepareStatement(query)) + { + String[] replaceParams = {"login", login, "password", password, "ip", ip}; + for (int i = 0; i < queryParams.length; i++) + { + s.setString(i + 1, CommonHelper.replace(queryParams[i], replaceParams)); + } + + // Execute SQL query + s.setQueryTimeout(MariaDBSourceConfig.TIMEOUT); + try (ResultSet set = s.executeQuery()) + { + return set.next() ? BCrypt.checkpw(password, "$2a" + set.getString(1).substring(3)) ? new AuthProviderResult(set.getString(2), SecurityHelper.randomStringToken()) : authError("Incorrect username or password") : authError("Incorrect username or password"); + } + } + } + + @Override + public void close() + { + mySQLHolder.close(); + } +} diff --git a/LaunchServer/source/auth/provider/MySQL8AuthProvider.java b/LaunchServer/source/auth/provider/MySQL8AuthProvider.java new file mode 100644 index 0000000..899ad4c --- /dev/null +++ b/LaunchServer/source/auth/provider/MySQL8AuthProvider.java @@ -0,0 +1,60 @@ +package launchserver.auth.provider; + +import launcher.helper.CommonHelper; +import launcher.helper.SecurityHelper; +import launcher.helper.VerifyHelper; +import launcher.serialize.config.entry.BlockConfigEntry; +import launcher.serialize.config.entry.ListConfigEntry; +import launcher.serialize.config.entry.StringConfigEntry; +import launchserver.auth.AuthException; +import launchserver.auth.MySQL8SourceConfig; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +public final class MySQL8AuthProvider extends AuthProvider +{ + private final MySQL8SourceConfig mySQLHolder; + private final String query; + private final String[] queryParams; + + MySQL8AuthProvider(BlockConfigEntry block) + { + super(block); + mySQLHolder = new MySQL8SourceConfig("authProviderPool", block); + + // Read query + query = VerifyHelper.verify(block.getEntryValue("query", StringConfigEntry.class), + VerifyHelper.NOT_EMPTY, "MySQL query can't be empty"); + queryParams = block.getEntry("queryParams", ListConfigEntry.class). + stream(StringConfigEntry.class).toArray(String[]::new); + } + + @Override + public AuthProviderResult auth(String login, String password, String ip) throws SQLException, AuthException + { + try (Connection c = mySQLHolder.getConnection(); PreparedStatement s = c.prepareStatement(query)) + { + String[] replaceParams = {"login", login, "password", password, "ip", ip}; + for (int i = 0; i < queryParams.length; i++) + { + s.setString(i + 1, CommonHelper.replace(queryParams[i], replaceParams)); + } + + // Execute SQL query + s.setQueryTimeout(MySQL8SourceConfig.TIMEOUT); + try (ResultSet set = s.executeQuery()) + { + return set.next() ? new AuthProviderResult(set.getString(1), SecurityHelper.randomStringToken()) : authError("Incorrect username or password"); + } + } + } + + @Override + public void close() + { + // Do nothing + } +} diff --git a/LaunchServer/source/auth/provider/MySQL8BcryptAuthProvider.java b/LaunchServer/source/auth/provider/MySQL8BcryptAuthProvider.java new file mode 100644 index 0000000..a6f6ecd --- /dev/null +++ b/LaunchServer/source/auth/provider/MySQL8BcryptAuthProvider.java @@ -0,0 +1,60 @@ +package launchserver.auth.provider; + +import launcher.helper.CommonHelper; +import launcher.helper.SecurityHelper; +import launcher.helper.VerifyHelper; +import launcher.serialize.config.entry.BlockConfigEntry; +import launcher.serialize.config.entry.ListConfigEntry; +import launcher.serialize.config.entry.StringConfigEntry; +import launchserver.auth.AuthException; +import launchserver.auth.MySQL8SourceConfig; +import org.mindrot.jbcrypt.BCrypt; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +public final class MySQL8BcryptAuthProvider extends AuthProvider +{ + private final MySQL8SourceConfig mySQLHolder; + private final String query; + private final String[] queryParams; + + MySQL8BcryptAuthProvider(BlockConfigEntry block) + { + super(block); + mySQLHolder = new MySQL8SourceConfig("authProviderPool", block); + + query = VerifyHelper.verify(block.getEntryValue("query", StringConfigEntry.class), + VerifyHelper.NOT_EMPTY, "MySQL query can't be empty"); + queryParams = block.getEntry("queryParams", ListConfigEntry.class). + stream(StringConfigEntry.class).toArray(String[]::new); + } + + @Override + public AuthProviderResult auth(String login, String password, String ip) throws SQLException, AuthException + { + try (Connection c = mySQLHolder.getConnection(); PreparedStatement s = c.prepareStatement(query)) + { + String[] replaceParams = {"login", login, "password", password, "ip", ip}; + for (int i = 0; i < queryParams.length; i++) + { + s.setString(i + 1, CommonHelper.replace(queryParams[i], replaceParams)); + } + + // Execute SQL query + s.setQueryTimeout(MySQL8SourceConfig.TIMEOUT); + try (ResultSet set = s.executeQuery()) + { + return set.next() ? BCrypt.checkpw(password, "$2a" + set.getString(1).substring(3)) ? new AuthProviderResult(set.getString(2), SecurityHelper.randomStringToken()) : authError("Incorrect username or password") : authError("Incorrect username or password"); + } + } + } + + @Override + public void close() + { + mySQLHolder.close(); + } +} diff --git a/build/libraries/mariadb.jar b/build/libraries/mariadb.jar new file mode 100644 index 0000000..4845a5b --- /dev/null +++ b/build/libraries/mariadb.jar Binary files differ diff --git a/build/libraries/mysql-8.jar b/build/libraries/mysql-8.jar new file mode 100644 index 0000000..c3b5f70 --- /dev/null +++ b/build/libraries/mysql-8.jar Binary files differ