diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/MySQLSourceConfig.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/MySQLSourceConfig.java index ec9d1e60..55ce1478 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/MySQLSourceConfig.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/MySQLSourceConfig.java @@ -11,7 +11,7 @@ import java.sql.Connection; import java.sql.SQLException; -public final class MySQLSourceConfig implements AutoCloseable { +public final class MySQLSourceConfig implements AutoCloseable, SQLSourceConfig { public static final int TIMEOUT = VerifyHelper.verifyInt( Integer.parseUnsignedInt(System.getProperty("launcher.mysql.idleTimeout", Integer.toString(5000))), diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/PostgreSQLSourceConfig.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/PostgreSQLSourceConfig.java index 621feea2..bb4c6e4a 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/PostgreSQLSourceConfig.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/PostgreSQLSourceConfig.java @@ -10,7 +10,7 @@ import java.sql.Connection; import java.sql.SQLException; -public final class PostgreSQLSourceConfig implements AutoCloseable { +public final class PostgreSQLSourceConfig implements AutoCloseable, SQLSourceConfig { public static final int TIMEOUT = VerifyHelper.verifyInt( Integer.parseUnsignedInt(System.getProperty("launcher.postgresql.idleTimeout", Integer.toString(5000))), VerifyHelper.POSITIVE, "launcher.postgresql.idleTimeout can't be <= 5000"); diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/SQLSourceConfig.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/SQLSourceConfig.java new file mode 100644 index 00000000..e6a1257b --- /dev/null +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/SQLSourceConfig.java @@ -0,0 +1,9 @@ +package pro.gravit.launchserver.auth; + +import java.sql.Connection; +import java.sql.SQLException; + +public interface SQLSourceConfig { + Connection getConnection() throws SQLException; + void close(); +} diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/AbstractSQLCoreProvider.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/AbstractSQLCoreProvider.java new file mode 100644 index 00000000..78401bc6 --- /dev/null +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/AbstractSQLCoreProvider.java @@ -0,0 +1,324 @@ +package pro.gravit.launchserver.auth.core; + +import io.jsonwebtoken.ExpiredJwtException; +import io.jsonwebtoken.JwtException; +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.SQLSourceConfig; +import pro.gravit.launchserver.auth.password.PasswordVerifier; +import pro.gravit.launchserver.helper.LegacySessionHelper; +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.time.Clock; +import java.time.LocalDateTime; +import java.util.UUID; + +public abstract class AbstractSQLCoreProvider extends AuthCoreProvider { + public transient Logger logger = LogManager.getLogger(); + + public abstract SQLSourceConfig getSQLConfig(); + + public int expireSeconds = 3600; + public String uuidColumn; + public String usernameColumn; + public String accessTokenColumn; + public String passwordColumn; + public String serverIDColumn; + public String table; + + public String permissionsTable; + public String permissionsPermissionColumn; + public String permissionsUUIDColumn; + + public PasswordVerifier passwordVerifier; + public String customQueryByUUIDSQL; + public String customQueryByUsernameSQL; + public String customQueryByLoginSQL; + public String customQueryPermissionsByUUIDSQL; + public String customUpdateAuthSQL; + public String customUpdateServerIdSQL; + // Prepared SQL queries + public transient String queryByUUIDSQL; + public transient String queryByUsernameSQL; + public transient String queryByLoginSQL; + public transient String queryPermissionsByUUIDSQL; + public transient String updateAuthSQL; + public transient String updateServerIDSQL; + + public transient LaunchServer server; + + @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 { + try { + var info = LegacySessionHelper.getJwtInfoFromAccessToken(accessToken, server.keyAgreementManager.ecdsaPublicKey); + var user = (SQLUser) getUserByUUID(info.uuid()); + if(user == null) { + return null; + } + return new SQLUserSession(user); + } catch (ExpiredJwtException e) { + throw new OAuthAccessTokenExpired(); + } catch (JwtException e) { + return null; + } + } + + @Override + public AuthManager.AuthReport refreshAccessToken(String refreshToken, AuthResponse.AuthContext context) { + String[] parts = refreshToken.split("\\."); + if(parts.length != 2) { + return null; + } + String username = parts[0]; + String token = parts[1]; + var user = (SQLUser) getUserByUsername(username); + if(user == null || user.password == null) { + return null; + } + var realToken = LegacySessionHelper.makeRefreshTokenFromPassword(username, user.password, server.keyAgreementManager.legacySalt); + if(!token.equals(realToken)) { + return null; + } + var accessToken = LegacySessionHelper.makeAccessJwtTokenFromString(user, LocalDateTime.now(Clock.systemUTC()).plusSeconds(expireSeconds), server.keyAgreementManager.ecdsaPrivateKey); + return new AuthManager.AuthReport(null, accessToken, refreshToken, expireSeconds * 1000L, new SQLUserSession(user)); + } + + @Override + public AuthManager.AuthReport authorize(String login, AuthResponse.AuthContext context, AuthRequest.AuthPasswordInterface password, boolean minecraftAccess) throws IOException { + SQLUser SQLUser = (SQLUser) getUserByLogin(login); + if(SQLUser == null) { + throw AuthException.wrongPassword(); + } + if(context != null) { + AuthPlainPassword plainPassword = (AuthPlainPassword) password; + if(plainPassword == null) { + throw AuthException.wrongPassword(); + } + if(!passwordVerifier.check(SQLUser.password, plainPassword.password)) { + throw AuthException.wrongPassword(); + } + } + SQLUserSession session = new SQLUserSession(SQLUser); + var accessToken = LegacySessionHelper.makeAccessJwtTokenFromString(SQLUser, LocalDateTime.now(Clock.systemUTC()).plusSeconds(expireSeconds), server.keyAgreementManager.ecdsaPrivateKey); + var refreshToken = SQLUser.username.concat(".").concat(LegacySessionHelper.makeRefreshTokenFromPassword(SQLUser.username, SQLUser.password, server.keyAgreementManager.legacySalt)); + if (minecraftAccess) { + String minecraftAccessToken = SecurityHelper.randomStringToken(); + updateAuth(SQLUser, minecraftAccessToken); + return AuthManager.AuthReport.ofOAuthWithMinecraft(minecraftAccessToken, accessToken, refreshToken, expireSeconds * 1000L, session); + } else { + return AuthManager.AuthReport.ofOAuth(accessToken, refreshToken, expireSeconds * 1000L, session); + } + } + + @Override + public void init(LaunchServer server) { + this.server = server; + if (getSQLConfig() == null) logger.error("SQLHolder 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 (table == null) logger.error("table cannot be null"); + // Prepare SQL queries + String userInfoCols = String.format("%s, %s, %s, %s, %s", uuidColumn, usernameColumn, accessTokenColumn, serverIDColumn, passwordColumn); + 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; + + queryPermissionsByUUIDSQL = customQueryPermissionsByUUIDSQL != null ? customQueryPermissionsByUUIDSQL : String.format("SELECT (%s) FROM %s WHERE %s=?", + permissionsPermissionColumn, permissionsTable, permissionsUUIDColumn); + + 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 = getSQLConfig().getConnection()) { + SQLUser SQLUser = (SQLUser) user; + SQLUser.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 = getSQLConfig().getConnection()) { + SQLUser SQLUser = (SQLUser) user; + SQLUser.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 { + getSQLConfig().close(); + } + + private SQLUser constructUser(ResultSet set) throws SQLException { + return set.next() ? new SQLUser(UUID.fromString(set.getString(uuidColumn)), set.getString(usernameColumn), + set.getString(accessTokenColumn), set.getString(serverIDColumn), set.getString(passwordColumn), requestPermissions(set.getString(uuidColumn))) : null; + } + + public ClientPermissions requestPermissions (String uuid) throws SQLException + { + try (Connection c = getSQLConfig().getConnection()) { + PreparedStatement s = c.prepareStatement(queryPermissionsByUUIDSQL); + s.setString(1, uuid); + s.setQueryTimeout(MySQLSourceConfig.TIMEOUT); + ResultSet set = s.executeQuery(); + ClientPermissions perms = new ClientPermissions(); + while (set.next()) { + perms.addPerm(set.getString(permissionsPermissionColumn)); + } + return perms; + } catch (SQLException e) { + throw new SQLException(e); + } + } + + private User query(String sql, String value) throws IOException { + try (Connection c = getSQLConfig().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 SQLUser implements User { + protected UUID uuid; + protected String username; + protected String accessToken; + protected String serverId; + protected String password; + protected ClientPermissions permissions; + + public SQLUser(UUID uuid, String username, String accessToken, String serverId, String password, ClientPermissions permissions) { + this.uuid = uuid; + this.username = username; + this.accessToken = accessToken; + this.serverId = serverId; + this.password = password; + this.permissions = permissions; + } + + @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 "SQLUser{" + + "uuid=" + uuid + + ", username='" + username + '\'' + + ", permissions=" + permissions + + '}'; + } + } + + public static class SQLUserSession implements UserSession { + private final SQLUser user; + private final String id; + + public SQLUserSession(SQLUser 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/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/MySQLCoreProvider.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/MySQLCoreProvider.java index bb402dde..8f8a7c6c 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/MySQLCoreProvider.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/MySQLCoreProvider.java @@ -11,6 +11,7 @@ import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.auth.AuthException; import pro.gravit.launchserver.auth.MySQLSourceConfig; +import pro.gravit.launchserver.auth.SQLSourceConfig; import pro.gravit.launchserver.auth.core.interfaces.UserHardware; import pro.gravit.launchserver.auth.core.interfaces.provider.AuthSupportHardware; import pro.gravit.launchserver.auth.core.interfaces.user.UserSupportHardware; @@ -32,28 +33,13 @@ import java.util.List; import java.util.UUID; -public class MySQLCoreProvider extends AuthCoreProvider implements AuthSupportHardware { - private transient final Logger logger = LogManager.getLogger(); +public class MySQLCoreProvider extends AbstractSQLCoreProvider implements AuthSupportHardware { public MySQLSourceConfig mySQLHolder; - public int expireSeconds = 3600; - public String uuidColumn; - public String usernameColumn; - public String accessTokenColumn; - public String passwordColumn; - public String serverIDColumn; public String hardwareIdColumn; - public String table; - public String tableHWID = "hwids"; public String tableHWIDLog = "hwidLog"; - public PasswordVerifier passwordVerifier; public double criticalCompareLevel = 1.0; - public String customQueryByUUIDSQL; - public String customQueryByUsernameSQL; - public String customQueryByLoginSQL; - public String customUpdateAuthSQL; - public String customUpdateServerIdSQL; private transient String sqlFindHardwareByPublicKey; private transient String sqlFindHardwareByData; private transient String sqlFindHardwareById; @@ -63,130 +49,16 @@ public class MySQLCoreProvider extends AuthCoreProvider implements AuthSupportHa private transient String sqlUpdateHardwareBanned; private transient String sqlUpdateUsers; private transient String sqlUsersByHwidId; - // Prepared SQL queries - private transient String queryByUUIDSQL; - private transient String queryByUsernameSQL; - private transient String queryByLoginSQL; - private transient String updateAuthSQL; - private transient String updateServerIDSQL; - - private transient LaunchServer server; @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 { - try { - var info = LegacySessionHelper.getJwtInfoFromAccessToken(accessToken, server.keyAgreementManager.ecdsaPublicKey); - var user = (MySQLUser) getUserByUUID(info.uuid()); - if(user == null) { - return null; - } - return new MySQLUserSession(user); - } catch (ExpiredJwtException e) { - throw new OAuthAccessTokenExpired(); - } catch (JwtException e) { - return null; - } - } - - @Override - public AuthManager.AuthReport refreshAccessToken(String refreshToken, AuthResponse.AuthContext context) { - String[] parts = refreshToken.split("\\."); - if(parts.length != 2) { - return null; - } - String username = parts[0]; - String token = parts[1]; - var user = (MySQLUser) getUserByUsername(username); - if(user == null || user.password == null) { - return null; - } - var realToken = LegacySessionHelper.makeRefreshTokenFromPassword(username, user.password, server.keyAgreementManager.legacySalt); - if(!token.equals(realToken)) { - return null; - } - var accessToken = LegacySessionHelper.makeAccessJwtTokenFromString(user, LocalDateTime.now(Clock.systemUTC()).plusSeconds(expireSeconds), server.keyAgreementManager.ecdsaPrivateKey); - return new AuthManager.AuthReport(null, accessToken, refreshToken, expireSeconds * 1000L, new MySQLUserSession(user)); - } - - @Override - public AuthManager.AuthReport authorize(String login, AuthResponse.AuthContext context, AuthRequest.AuthPasswordInterface password, boolean minecraftAccess) throws IOException { - MySQLUser mySQLUser = (MySQLUser) getUserByLogin(login); - if(mySQLUser == null) { - throw AuthException.wrongPassword(); - } - if(context != null) { - AuthPlainPassword plainPassword = (AuthPlainPassword) password; - if(plainPassword == null) { - throw AuthException.wrongPassword(); - } - if(!passwordVerifier.check(mySQLUser.password, plainPassword.password)) { - throw AuthException.wrongPassword(); - } - } - MySQLUserSession session = new MySQLUserSession(mySQLUser); - var accessToken = LegacySessionHelper.makeAccessJwtTokenFromString(mySQLUser, LocalDateTime.now(Clock.systemUTC()).plusSeconds(expireSeconds), server.keyAgreementManager.ecdsaPrivateKey); - var refreshToken = mySQLUser.username.concat(".").concat(LegacySessionHelper.makeRefreshTokenFromPassword(mySQLUser.username, mySQLUser.password, server.keyAgreementManager.legacySalt)); - if (minecraftAccess) { - String minecraftAccessToken = SecurityHelper.randomStringToken(); - updateAuth(mySQLUser, minecraftAccessToken); - return AuthManager.AuthReport.ofOAuthWithMinecraft(minecraftAccessToken, accessToken, refreshToken, expireSeconds * 1000L, session); - } else { - return AuthManager.AuthReport.ofOAuth(accessToken, refreshToken, expireSeconds * 1000L, session); - } + public SQLSourceConfig getSQLConfig() { + return mySQLHolder; } @Override public void init(LaunchServer server) { - this.server = server; - if (mySQLHolder == null) logger.error("mySQLHolder 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 + super.init(server); 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); String hardwareInfoCols = "id, hwDiskId, baseboardSerialNumber, displayId, bitness, totalMemory, logicalProcessors, physicalProcessors, processorMaxFreq, battery, id, graphicCard, banned, publicKey"; if (sqlFindHardwareByPublicKey == null) sqlFindHardwareByPublicKey = String.format("SELECT %s FROM %s WHERE `publicKey` = ?", hardwareInfoCols, tableHWID); @@ -206,43 +78,9 @@ public void init(LaunchServer server) { sqlUpdateUsers = String.format("UPDATE %s SET `%s` = ? WHERE `%s` = ?", table, hardwareIdColumn, uuidColumn); } - protected boolean updateAuth(User user, String accessToken) throws IOException { - try (Connection c = mySQLHolder.getConnection()) { - MySQLUser mySQLUser = (MySQLUser) user; - mySQLUser.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 = mySQLHolder.getConnection()) { - MySQLUser mySQLUser = (MySQLUser) user; - mySQLUser.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 { - mySQLHolder.close(); - } - private MySQLUser constructUser(ResultSet set) throws SQLException { return set.next() ? new MySQLUser(UUID.fromString(set.getString(uuidColumn)), set.getString(usernameColumn), - set.getString(accessTokenColumn), set.getString(serverIDColumn), set.getString(passwordColumn), new ClientPermissions(), set.getLong(hardwareIdColumn)) : null; + set.getString(accessTokenColumn), set.getString(serverIDColumn), set.getString(passwordColumn), requestPermissions(set.getString(uuidColumn)), set.getLong(hardwareIdColumn)) : null; } private MySQLUserHardware fetchHardwareInfo(ResultSet set) throws SQLException, IOException { @@ -270,20 +108,6 @@ private void setUserHardwareId(Connection connection, UUID uuid, long hwidId) th s.setString(2, uuid.toString()); s.executeUpdate(); } - - private User query(String sql, String value) throws IOException { - try (Connection c = mySQLHolder.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); - } - } - @Override public UserHardware getHardwareInfoByPublicKey(byte[] publicKey) { try (Connection connection = mySQLHolder.getConnection()) { @@ -371,8 +195,8 @@ public UserHardware createHardwareInfo(HardwareReportRequest.HardwareInfo hardwa @Override public void connectUserAndHardware(UserSession userSession, UserHardware hardware) { - MySQLUserSession mySQLUserSession = (MySQLUserSession) userSession; - MySQLUser mySQLUser = mySQLUserSession.user; + SQLUserSession mySQLUserSession = (SQLUserSession) userSession; + MySQLUser mySQLUser = (MySQLUser) mySQLUserSession.getUser(); MySQLUserHardware mySQLUserHardware = (MySQLUserHardware) hardware; if (mySQLUser.hwidId == mySQLUserHardware.id) return; mySQLUser.hwidId = mySQLUserHardware.id; @@ -488,51 +312,14 @@ public String toString() { } } - public class MySQLUser implements User, UserSupportHardware { - protected UUID uuid; - protected String username; - protected String accessToken; - protected String serverId; - protected String password; - protected ClientPermissions permissions; + public class MySQLUser extends SQLUser implements UserSupportHardware { protected long hwidId; protected transient MySQLUserHardware hardware; public MySQLUser(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; + super(uuid, username, accessToken, serverId, password, 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 UserHardware getHardware() { if (hardware != null) return hardware; @@ -551,29 +338,4 @@ public String toString() { '}'; } } - - public static class MySQLUserSession implements UserSession { - private final MySQLUser user; - private final String id; - - public MySQLUserSession(MySQLUser 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/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/PostgresSQLCoreProvider.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/PostgresSQLCoreProvider.java index 9a7c731a..0e5d9b67 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/PostgresSQLCoreProvider.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/PostgresSQLCoreProvider.java @@ -1,297 +1,14 @@ package pro.gravit.launchserver.auth.core; -import io.jsonwebtoken.ExpiredJwtException; -import io.jsonwebtoken.JwtException; -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.helper.LegacySessionHelper; -import pro.gravit.launchserver.manangers.AuthManager; -import pro.gravit.launchserver.socket.response.auth.AuthResponse; -import pro.gravit.utils.helper.SecurityHelper; +import pro.gravit.launchserver.auth.SQLSourceConfig; -import java.io.IOException; -import java.sql.*; -import java.time.Clock; -import java.time.LocalDateTime; -import java.util.UUID; - -public class PostgresSQLCoreProvider extends AuthCoreProvider { - private transient final Logger logger = LogManager.getLogger(); +public class PostgresSQLCoreProvider extends AbstractSQLCoreProvider { public PostgreSQLSourceConfig postgresSQLHolder; - public int expireSeconds = 3600; - public String uuidColumn; - public String usernameColumn; - public String accessTokenColumn; - public String passwordColumn; - public String serverIDColumn; - 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; - - private transient LaunchServer server; - @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 { - try { - var info = LegacySessionHelper.getJwtInfoFromAccessToken(accessToken, server.keyAgreementManager.ecdsaPublicKey); - var user = (PostgresSQLUser) getUserByUUID(info.uuid()); - if(user == null) { - return null; - } - return new PostgresSQLCoreProvider.MySQLUserSession(user); - } catch (ExpiredJwtException e) { - throw new OAuthAccessTokenExpired(); - } catch (JwtException e) { - return null; - } - } - - @Override - public AuthManager.AuthReport refreshAccessToken(String refreshToken, AuthResponse.AuthContext context) { - String[] parts = refreshToken.split("\\."); - if(parts.length != 2) { - return null; - } - String username = parts[0]; - String token = parts[1]; - var user = (PostgresSQLUser) getUserByUsername(username); - if(user == null || user.password == null) { - return null; - } - var realToken = LegacySessionHelper.makeRefreshTokenFromPassword(username, user.password, server.keyAgreementManager.legacySalt); - if(!token.equals(realToken)) { - return null; - } - var accessToken = LegacySessionHelper.makeAccessJwtTokenFromString(user, LocalDateTime.now(Clock.systemUTC()).plusSeconds(expireSeconds), server.keyAgreementManager.ecdsaPrivateKey); - return new AuthManager.AuthReport(null, accessToken, refreshToken, expireSeconds * 1000L, new PostgresSQLCoreProvider.MySQLUserSession(user)); - } - - @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); - var accessToken = LegacySessionHelper.makeAccessJwtTokenFromString(postgresSQLUser, LocalDateTime.now(Clock.systemUTC()).plusSeconds(expireSeconds), server.keyAgreementManager.ecdsaPrivateKey); - var refreshToken = postgresSQLUser.username.concat(".").concat(LegacySessionHelper.makeRefreshTokenFromPassword(postgresSQLUser.username, postgresSQLUser.password, server.keyAgreementManager.legacySalt)); - if (minecraftAccess) { - String minecraftAccessToken = SecurityHelper.randomStringToken(); - updateAuth(postgresSQLUser, minecraftAccessToken); - return AuthManager.AuthReport.ofOAuthWithMinecraft(minecraftAccessToken, accessToken, refreshToken, expireSeconds * 1000L, session); - } else { - return AuthManager.AuthReport.ofOAuth(accessToken, refreshToken, expireSeconds * 1000L, session); - } - } - - @Override - public void init(LaunchServer server) { - this.server = 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 (table == null) logger.error("table cannot be null"); - // Prepare SQL queries - String userInfoCols = String.format("%s, %s, %s, %s, %s", uuidColumn, usernameColumn, accessTokenColumn, serverIDColumn, passwordColumn); - 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()) : 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; - - public PostgresSQLUser(UUID uuid, String username, String accessToken, String serverId, String password, ClientPermissions permissions) { - this.uuid = uuid; - this.username = username; - this.accessToken = accessToken; - this.serverId = serverId; - this.password = password; - this.permissions = permissions; - } - - @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 + - '}'; - } - } - - 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; - } + public SQLSourceConfig getSQLConfig() { + return postgresSQLHolder; } }