From 796b2e25333d4cccf256794766f0d596f7bb9f60 Mon Sep 17 00:00:00 2001 From: Gravita Date: Fri, 10 Dec 2021 20:45:37 +0700 Subject: [PATCH] [FEATURE] Postgresql support --- .../launchserver/auth/AuthProviderPair.java | 3 +- .../auth/core/AuthCoreProvider.java | 1 + .../auth/core/PostgresSQLCoreProvider.java | 265 ++++++++++++++++++ props.gradle | 18 +- 4 files changed, 277 insertions(+), 10 deletions(-) create mode 100644 LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/PostgresSQLCoreProvider.java diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/AuthProviderPair.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/AuthProviderPair.java index ff58cc50..296717a9 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/AuthProviderPair.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/AuthProviderPair.java @@ -5,6 +5,7 @@ import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.auth.core.AuthCoreProvider; import pro.gravit.launchserver.auth.core.MySQLCoreProvider; +import pro.gravit.launchserver.auth.core.PostgresSQLCoreProvider; import pro.gravit.launchserver.auth.texture.TextureProvider; import java.io.IOException; @@ -39,7 +40,7 @@ public static Set getFeatures(Class clazz) { public void internalShowOAuthWarnMessage() { if(!warnOAuthShow) { - if(!(core instanceof MySQLCoreProvider)) { // MySQL upgraded later + if(!(core instanceof MySQLCoreProvider) && !(core instanceof PostgresSQLCoreProvider)) { // MySQL and PostgreSQL upgraded later logger.warn("AuthCoreProvider {} ({}) not supported OAuth. Legacy session system may be removed in next release", name, core.getClass().getName()); } warnOAuthShow = true; diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/AuthCoreProvider.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/AuthCoreProvider.java index cb56f9c9..84d5f90e 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/AuthCoreProvider.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/AuthCoreProvider.java @@ -43,6 +43,7 @@ public static void registerProviders() { if (!registredProviders) { providers.register("reject", RejectAuthCoreProvider.class); providers.register("mysql", MySQLCoreProvider.class); + providers.register("postgresql", PostgresSQLCoreProvider.class); providers.register("memory", MemoryAuthCoreProvider.class); providers.register("http", HttpAuthCoreProvider.class); registredProviders = true; diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/PostgresSQLCoreProvider.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/PostgresSQLCoreProvider.java new file mode 100644 index 00000000..34323cd9 --- /dev/null +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/PostgresSQLCoreProvider.java @@ -0,0 +1,265 @@ +package pro.gravit.launchserver.auth.core; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import pro.gravit.launcher.ClientPermissions; +import pro.gravit.launcher.request.auth.AuthRequest; +import pro.gravit.launcher.request.auth.password.AuthPlainPassword; +import pro.gravit.launchserver.LaunchServer; +import pro.gravit.launchserver.auth.AuthException; +import pro.gravit.launchserver.auth.MySQLSourceConfig; +import pro.gravit.launchserver.auth.PostgreSQLSourceConfig; +import pro.gravit.launchserver.auth.password.PasswordVerifier; +import pro.gravit.launchserver.manangers.AuthManager; +import pro.gravit.launchserver.socket.response.auth.AuthResponse; +import pro.gravit.utils.helper.SecurityHelper; + +import java.io.IOException; +import java.sql.*; +import java.util.UUID; + +public class PostgresSQLCoreProvider extends AuthCoreProvider { + private transient final Logger logger = LogManager.getLogger(); + public PostgreSQLSourceConfig postgresSQLHolder; + + public String uuidColumn; + public String usernameColumn; + public String accessTokenColumn; + public String passwordColumn; + public String serverIDColumn; + public String hardwareIdColumn; + public String table; + + public PasswordVerifier passwordVerifier; + public String customQueryByUUIDSQL; + public String customQueryByUsernameSQL; + public String customQueryByLoginSQL; + public String customUpdateAuthSQL; + public String customUpdateServerIdSQL; + // Prepared SQL queries + private transient String queryByUUIDSQL; + private transient String queryByUsernameSQL; + private transient String queryByLoginSQL; + private transient String updateAuthSQL; + private transient String updateServerIDSQL; + + @Override + public User getUserByUsername(String username) { + try { + return query(queryByUsernameSQL, username); + } catch (IOException e) { + logger.error("SQL error", e); + return null; + } + } + + @Override + public User getUserByUUID(UUID uuid) { + try { + return query(queryByUUIDSQL, uuid.toString()); + } catch (IOException e) { + logger.error("SQL error", e); + return null; + } + } + + @Override + public User getUserByLogin(String login) { + try { + return query(queryByLoginSQL, login); + } catch (IOException e) { + logger.error("SQL error", e); + return null; + } + } + + @Override + public UserSession getUserSessionByOAuthAccessToken(String accessToken) throws OAuthAccessTokenExpired { + return null; + } + + @Override + public AuthManager.AuthReport refreshAccessToken(String refreshToken, AuthResponse.AuthContext context) { + return null; + } + + @Override + public AuthManager.AuthReport authorize(String login, AuthResponse.AuthContext context, AuthRequest.AuthPasswordInterface password, boolean minecraftAccess) throws IOException { + PostgresSQLUser postgresSQLUser = (PostgresSQLUser) getUserByLogin(login); + if(postgresSQLUser == null) { + throw AuthException.wrongPassword(); + } + if(context != null) { + AuthPlainPassword plainPassword = (AuthPlainPassword) password; + if(plainPassword == null) { + throw AuthException.wrongPassword(); + } + if(!passwordVerifier.check(postgresSQLUser.password, plainPassword.password)) { + throw AuthException.wrongPassword(); + } + } + MySQLUserSession session = new MySQLUserSession(postgresSQLUser); + if (minecraftAccess) { + String minecraftAccessToken = SecurityHelper.randomStringToken(); + updateAuth(postgresSQLUser, minecraftAccessToken); + return AuthManager.AuthReport.ofMinecraftAccessToken(minecraftAccessToken, session); + } else { + return AuthManager.AuthReport.ofMinecraftAccessToken(null, session); + } + } + + @Override + public void init(LaunchServer server) { + if (postgresSQLHolder == null) logger.error("postgresSQLHolder cannot be null"); + if (uuidColumn == null) logger.error("uuidColumn cannot be null"); + if (usernameColumn == null) logger.error("usernameColumn cannot be null"); + if (accessTokenColumn == null) logger.error("accessTokenColumn cannot be null"); + if (serverIDColumn == null) logger.error("serverIDColumn cannot be null"); + if (hardwareIdColumn == null) logger.error("hardwareIdColumn cannot be null"); + if (table == null) logger.error("table cannot be null"); + // Prepare SQL queries + String userInfoCols = String.format("%s, %s, %s, %s, %s, %s", uuidColumn, usernameColumn, accessTokenColumn, serverIDColumn, passwordColumn, hardwareIdColumn); + queryByUUIDSQL = customQueryByUUIDSQL != null ? customQueryByUUIDSQL : String.format("SELECT %s FROM %s WHERE %s=? LIMIT 1", userInfoCols, + table, uuidColumn); + queryByUsernameSQL = customQueryByUsernameSQL != null ? customQueryByUsernameSQL : String.format("SELECT %s FROM %s WHERE %s=? LIMIT 1", + userInfoCols, table, usernameColumn); + queryByLoginSQL = customQueryByLoginSQL != null ? customQueryByLoginSQL : queryByUsernameSQL; + + updateAuthSQL = customUpdateAuthSQL != null ? customUpdateAuthSQL : String.format("UPDATE %s SET %s=?, %s=NULL WHERE %s=?", + table, accessTokenColumn, serverIDColumn, uuidColumn); + updateServerIDSQL = customUpdateServerIdSQL != null ? customUpdateServerIdSQL : String.format("UPDATE %s SET %s=? WHERE %s=?", + table, serverIDColumn, uuidColumn); + } + + protected boolean updateAuth(User user, String accessToken) throws IOException { + try (Connection c = postgresSQLHolder.getConnection()) { + PostgresSQLUser postgresSQLUser = (PostgresSQLUser) user; + postgresSQLUser.accessToken = accessToken; + PreparedStatement s = c.prepareStatement(updateAuthSQL); + s.setString(1, accessToken); + s.setString(2, user.getUUID().toString()); + s.setQueryTimeout(MySQLSourceConfig.TIMEOUT); + return s.executeUpdate() > 0; + } catch (SQLException e) { + throw new IOException(e); + } + } + + @Override + protected boolean updateServerID(User user, String serverID) throws IOException { + try (Connection c = postgresSQLHolder.getConnection()) { + PostgresSQLUser postgresSQLUser = (PostgresSQLUser) user; + postgresSQLUser.serverId = serverID; + PreparedStatement s = c.prepareStatement(updateServerIDSQL); + s.setString(1, serverID); + s.setString(2, user.getUUID().toString()); + s.setQueryTimeout(MySQLSourceConfig.TIMEOUT); + return s.executeUpdate() > 0; + } catch (SQLException e) { + throw new IOException(e); + } + } + + @Override + public void close() throws IOException { + postgresSQLHolder.close(); + } + + private PostgresSQLUser constructUser(ResultSet set) throws SQLException { + return set.next() ? new PostgresSQLUser(UUID.fromString(set.getString(uuidColumn)), set.getString(usernameColumn), + set.getString(accessTokenColumn), set.getString(serverIDColumn), set.getString(passwordColumn), new ClientPermissions(), set.getLong(hardwareIdColumn)) : null; + } + + private User query(String sql, String value) throws IOException { + try (Connection c = postgresSQLHolder.getConnection()) { + PreparedStatement s = c.prepareStatement(sql); + s.setString(1, value); + s.setQueryTimeout(MySQLSourceConfig.TIMEOUT); + try (ResultSet set = s.executeQuery()) { + return constructUser(set); + } + } catch (SQLException e) { + throw new IOException(e); + } + } + + public static class PostgresSQLUser implements User { + protected UUID uuid; + protected String username; + protected String accessToken; + protected String serverId; + protected String password; + protected ClientPermissions permissions; + protected long hwidId; + + public PostgresSQLUser(UUID uuid, String username, String accessToken, String serverId, String password, ClientPermissions permissions, long hwidId) { + this.uuid = uuid; + this.username = username; + this.accessToken = accessToken; + this.serverId = serverId; + this.password = password; + this.permissions = permissions; + this.hwidId = hwidId; + } + + @Override + public String getUsername() { + return username; + } + + @Override + public UUID getUUID() { + return uuid; + } + + @Override + public String getServerId() { + return serverId; + } + + @Override + public String getAccessToken() { + return accessToken; + } + + @Override + public ClientPermissions getPermissions() { + return permissions; + } + + @Override + public String toString() { + return "PostgresSQLUser{" + + "uuid=" + uuid + + ", username='" + username + '\'' + + ", permissions=" + permissions + + ", hwidId=" + hwidId + + '}'; + } + } + + public static class MySQLUserSession implements UserSession { + private final PostgresSQLUser user; + private final String id; + + public MySQLUserSession(PostgresSQLUser user) { + this.user = user; + this.id = user.username; + } + + @Override + public String getID() { + return id; + } + + @Override + public User getUser() { + return user; + } + + @Override + public long getExpireIn() { + return 0; + } + } +} diff --git a/props.gradle b/props.gradle index 5a85c822..330903d1 100644 --- a/props.gradle +++ b/props.gradle @@ -1,19 +1,19 @@ project.ext { verAsm = '9.2' - verNetty = '4.1.68.Final' - verOshiCore = '5.8.0' - verJunit = '5.7.2' + verNetty = '4.1.70.Final' + verOshiCore = '5.8.5' + verJunit = '5.8.2' verGuavaC = '30.1.1-jre' verJansi = '2.3.4' - verJline = '3.20.0' + verJline = '3.21.0' verJwt = '0.11.2' - verBcprov = '1.69' + verBcprov = '1.70' verGson = '2.8.9' - verBcpkix = '1.69' + verBcpkix = '1.70' verSlf4j = '1.7.32' - verLog4j = '2.14.1' - verMySQLConn = '8.0.26' - verPostgreSQLConn = '42.2.24' + verLog4j = '2.15.0' + verMySQLConn = '8.0.27' + verPostgreSQLConn = '42.3.1' verProguard = '7.2.0-beta2' verLaunch4j = '3.14' verHibernate = '5.5.6.Final'