diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml deleted file mode 100644 index 200ef183..00000000 --- a/.github/workflows/codeql-analysis.yml +++ /dev/null @@ -1,70 +0,0 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -# -# ******** NOTE ******** -# We have attempted to detect the languages in your repository. Please check -# the `language` matrix defined below to confirm you have the correct set of -# supported CodeQL languages. -# -name: "CodeQL" - -on: - push: - branches: [ master, dev ] - pull_request: - # The branches below must be a subset of the branches above - branches: [ master, dev ] - schedule: - - cron: '28 4 * * 0' - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - - strategy: - fail-fast: false - matrix: - language: [ 'java' ] - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] - # Learn more: - # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed - - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v1 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main - - name: Set up JDK 11 - uses: actions/setup-java@v1 - with: - java-version: 11 - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v1 - - # ℹī¸ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl - - # ✏ī¸ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language - - #- run: | - # make bootstrap - # make release - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 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..747f089e --- /dev/null +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/PostgresSQLCoreProvider.java @@ -0,0 +1,260 @@ +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 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 (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; + } + } +} diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/ClientPermissions.java b/LauncherAPI/src/main/java/pro/gravit/launcher/ClientPermissions.java index 96938403..d590ffb6 100644 --- a/LauncherAPI/src/main/java/pro/gravit/launcher/ClientPermissions.java +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/ClientPermissions.java @@ -56,6 +56,10 @@ public synchronized void compile() { if (available != null) { return; } + if (perms == null) { + perms = new ArrayList<>(0); + + } available = new ArrayList<>(perms.size()); for (String a : perms) { available.add(new PermissionPattern(a)); diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/profiles/ClientProfile.java b/LauncherAPI/src/main/java/pro/gravit/launcher/profiles/ClientProfile.java index 816dc942..ffe60874 100644 --- a/LauncherAPI/src/main/java/pro/gravit/launcher/profiles/ClientProfile.java +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/profiles/ClientProfile.java @@ -481,7 +481,9 @@ public enum Version { MC1164("1.16.4", 754), MC1165("1.16.5", 754), MC117("1.17", 755), - MC1171("1.17.1", 756); + MC1171("1.17.1", 756), + MC118("1.18", 757), + MC1181("1.18.1", 757); private static final Map VERSIONS; static { diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/profiles/optional/OptionalView.java b/LauncherAPI/src/main/java/pro/gravit/launcher/profiles/optional/OptionalView.java index 11a6afae..c46a35f5 100644 --- a/LauncherAPI/src/main/java/pro/gravit/launcher/profiles/optional/OptionalView.java +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/profiles/optional/OptionalView.java @@ -80,7 +80,7 @@ public void enable(OptionalFile file, boolean manual, BiConsumer { public static final int MAJOR = 5; public static final int MINOR = 2; - public static final int PATCH = 6; + public static final int PATCH = 7; public static final int BUILD = 1; public static final Version.Type RELEASE = Type.STABLE; public final int major; diff --git a/LauncherCore/src/main/java/pro/gravit/utils/helper/IOHelper.java b/LauncherCore/src/main/java/pro/gravit/utils/helper/IOHelper.java index 313945d3..ba16d237 100644 --- a/LauncherCore/src/main/java/pro/gravit/utils/helper/IOHelper.java +++ b/LauncherCore/src/main/java/pro/gravit/utils/helper/IOHelper.java @@ -430,11 +430,15 @@ public static Path resolveIncremental(Path dir, String name, String extension) { } public static Path resolveJavaBin(Path javaDir) { + return resolveJavaBin(javaDir, false); + } + + public static Path resolveJavaBin(Path javaDir, boolean isConsole) { // Get Java binaries path Path javaBinDir = (javaDir == null ? JVM_DIR : javaDir).resolve("bin"); // Verify has "javaw.exe" file - if (!LogHelper.isDebugEnabled()) { + if (!isConsole && !LogHelper.isDebugEnabled()) { Path javawExe = javaBinDir.resolve("javaw.exe"); if (isFile(javawExe)) return javawExe; diff --git a/ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapper.java b/ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapper.java index a0505386..5400b2b4 100644 --- a/ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapper.java +++ b/ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapper.java @@ -141,14 +141,16 @@ public void run(String... args) throws Throwable { } Class mainClass; if (config.classpath != null && !config.classpath.isEmpty()) { - if (!ServerAgent.isAgentStarted()) { - LogHelper.warning("JavaAgent not found. Using URLClassLoader"); + if(config.classLoaderConfig == ClientProfile.ClassLoaderConfig.LAUNCHER) { URL[] urls = config.classpath.stream().map(Paths::get).map(IOHelper::toURL).toArray(URL[]::new); ucp = new PublicURLClassLoader(urls); Thread.currentThread().setContextClassLoader(ucp); loader = ucp; - } else { - LogHelper.info("Found %d custom classpath elements", config.classpath.size()); + } else if(config.classLoaderConfig == ClientProfile.ClassLoaderConfig.AGENT) { + if (!ServerAgent.isAgentStarted()) { + LogHelper.error("JavaAgent not found"); + System.exit(-1); + } for (String c : config.classpath) ServerAgent.addJVMClassPath(c); } @@ -207,6 +209,7 @@ public Config getDefaultConfig() { newConfig.args = new ArrayList<>(); newConfig.classpath = new ArrayList<>(); newConfig.address = "ws://localhost:9274/api"; + newConfig.classLoaderConfig = ClientProfile.ClassLoaderConfig.SYSTEM_ARGS; newConfig.env = LauncherConfig.LauncherEnvironment.STD; return newConfig; } @@ -219,6 +222,7 @@ public static final class Config { public boolean autoloadLibraries; public String logFile; public List classpath; + public ClientProfile.ClassLoaderConfig classLoaderConfig; public String librariesDir; public String mainclass; public List args; diff --git a/ServerWrapper/src/main/java/pro/gravit/launcher/server/setup/ServerWrapperSetup.java b/ServerWrapper/src/main/java/pro/gravit/launcher/server/setup/ServerWrapperSetup.java index b173e439..37e1f8b7 100644 --- a/ServerWrapper/src/main/java/pro/gravit/launcher/server/setup/ServerWrapperSetup.java +++ b/ServerWrapper/src/main/java/pro/gravit/launcher/server/setup/ServerWrapperSetup.java @@ -1,5 +1,6 @@ package pro.gravit.launcher.server.setup; +import pro.gravit.launcher.profiles.ClientProfile; import pro.gravit.launcher.request.Request; import pro.gravit.launcher.request.websockets.StdWebSocketService; import pro.gravit.launcher.server.ServerWrapper; @@ -57,6 +58,7 @@ public void run() throws Exception { System.out.println("Print your server name:"); wrapper.config.serverName = commands.commandHandler.readLine(); wrapper.config.mainclass = mainClassName; + boolean altMode = false; for (int i = 0; i < 10; ++i) { if(!Request.isAvailable() || Request.getRequestService().isClosed()) { System.out.println("Print launchserver websocket host( ws://host:port/api ):"); @@ -85,6 +87,12 @@ public void run() throws Exception { } } } + if(wrapper.profile != null && wrapper.profile.getVersion().compareTo(ClientProfile.Version.MC118) >= 0) { + LogHelper.info("Switch to alternative start mode (1.18)"); + wrapper.config.classpath.add(jarName); + wrapper.config.classLoaderConfig = ClientProfile.ClassLoaderConfig.LAUNCHER; + altMode = true; + } wrapper.saveConfig(); LogHelper.info("Generate start script"); Path startScript; @@ -99,8 +107,9 @@ public void run() throws Exception { if (JVMHelper.OS_TYPE == JVMHelper.OS.LINUX) { writer.append("#!/bin/bash\n\n"); } - writer.append(IOHelper.resolveJavaBin(Paths.get(System.getProperty("java.home"))).toAbsolutePath().toString()); - writer.append(" "); + writer.append("\""); + writer.append(IOHelper.resolveJavaBin(Paths.get(System.getProperty("java.home")), true).toAbsolutePath().toString()); + writer.append("\" "); if (mainClassName.contains("bungee")) { LogHelper.info("Found BungeeCord mainclass. Modules dir change to modules_srv"); writer.append(JVMHelper.jvmProperty("serverwrapper.modulesDir", "modules_srv")); @@ -115,10 +124,12 @@ public void run() throws Exception { writer.append("-cp "); String pathServerWrapper = IOHelper.getCodeSource(ServerWrapper.class).getFileName().toString(); writer.append(pathServerWrapper); - if (JVMHelper.OS_TYPE == JVMHelper.OS.MUSTDIE) { - writer.append(";"); - } else writer.append(":"); - writer.append(jarName); + if(!altMode) { + if (JVMHelper.OS_TYPE == JVMHelper.OS.MUSTDIE) { + writer.append(";"); + } else writer.append(":"); + writer.append(jarName); + } writer.append(" "); writer.append(ServerWrapper.class.getName()); writer.append("\n"); diff --git a/build.gradle b/build.gradle index cfac7e79..8d1bbf49 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ id 'org.openjfx.javafxplugin' version '0.0.10' apply false } group = 'pro.gravit.launcher' -version = '5.2.6 ' +version = '5.2.7' apply from: 'props.gradle' 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'