aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPetr Ilin <hevav@hevav.dev>2022-12-24 20:19:03 +0300
committerPetr Ilin <hevav@hevav.dev>2022-12-24 20:19:03 +0300
commit06f2a95f5d8c4eb6cd13656cd6dbb0322c4f3cdc (patch)
tree5e1588441e1390b695146ebc4ff99c2c9a09557e
parent8ff7f25c006dd367e1b35e1d13e105900b7c3d60 (diff)
downloadLimboAuth-06f2a95f5d8c4eb6cd13656cd6dbb0322c4f3cdc.tar.gz
LimboAuth-06f2a95f5d8c4eb6cd13656cd6dbb0322c4f3cdc.tar.bz2
LimboAuth-06f2a95f5d8c4eb6cd13656cd6dbb0322c4f3cdc.zip
H2 v1->v2 migration, external database libraries
-rw-r--r--build.gradle7
-rw-r--r--config/spotbugs/suppressions.xml3
-rw-r--r--src/main/java/net/elytrium/limboauth/LimboAuth.java49
-rw-r--r--src/main/java/net/elytrium/limboauth/Settings.java2
-rw-r--r--src/main/java/net/elytrium/limboauth/dependencies/BaseLibrary.java94
-rw-r--r--src/main/java/net/elytrium/limboauth/dependencies/DatabaseLibrary.java149
-rw-r--r--src/main/java/net/elytrium/limboauth/dependencies/IsolatedClassLoader.java32
7 files changed, 291 insertions, 45 deletions
diff --git a/build.gradle b/build.gradle
index f63c6da..d2f7bf8 100644
--- a/build.gradle
+++ b/build.gradle
@@ -51,10 +51,6 @@ dependencies {
implementation("com.j256.ormlite:ormlite-jdbc:6.1")
- implementation("com.h2database:h2:1.4.200")
- implementation('mysql:mysql-connector-java:8.0.31')
- implementation('org.postgresql:postgresql:42.5.1')
-
implementation("org.bstats:bstats-velocity:3.0.0")
implementation("de.mkammerer:argon2-jvm-nolibs:2.11")
@@ -100,7 +96,6 @@ shadowJar {
relocate("at.favre.lib", "net.elytrium.limboauth.thirdparty.at.favre.lib")
relocate("com.j256.ormlite", "net.elytrium.limboauth.thirdparty.com.j256.ormlite")
- relocate("com.mysql", "net.elytrium.limboauth.thirdparty.com.mysql")
relocate("com.sun.jna", "net.elytrium.limboauth.thirdparty.com.sun.jna") {
exclude("com.sun.jna.Native") // For compatibility with native methods.
}
@@ -108,8 +103,6 @@ shadowJar {
relocate("dev.samstevens.totp", "net.elytrium.limboauth.thirdparty.dev.samstevens.totp")
relocate("org.apache.commons.codec", "net.elytrium.limboauth.thirdparty.org.apache.commons.codec")
relocate("org.bstats", "net.elytrium.limboauth.thirdparty.org.bstats")
- relocate("org.h2", "net.elytrium.limboauth.thirdparty.org.h2")
- relocate("org.postgresql", "net.elytrium.limboauth.thirdparty.org.postgresql")
}
license {
diff --git a/config/spotbugs/suppressions.xml b/config/spotbugs/suppressions.xml
index 833396e..9e62805 100644
--- a/config/spotbugs/suppressions.xml
+++ b/config/spotbugs/suppressions.xml
@@ -14,4 +14,7 @@
<Match>
<Bug pattern="RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE"/>
</Match>
+ <Match>
+ <Bug pattern="DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED"/>
+ </Match>
</FindBugsFilter>
diff --git a/src/main/java/net/elytrium/limboauth/LimboAuth.java b/src/main/java/net/elytrium/limboauth/LimboAuth.java
index a42322a..46b0864 100644
--- a/src/main/java/net/elytrium/limboauth/LimboAuth.java
+++ b/src/main/java/net/elytrium/limboauth/LimboAuth.java
@@ -26,8 +26,8 @@ import com.j256.ormlite.dao.DaoManager;
import com.j256.ormlite.dao.GenericRawResults;
import com.j256.ormlite.db.DatabaseType;
import com.j256.ormlite.field.FieldType;
-import com.j256.ormlite.jdbc.JdbcPooledConnectionSource;
import com.j256.ormlite.stmt.QueryBuilder;
+import com.j256.ormlite.support.ConnectionSource;
import com.j256.ormlite.table.TableInfo;
import com.j256.ormlite.table.TableUtils;
import com.velocitypowered.api.command.CommandManager;
@@ -90,6 +90,7 @@ import net.elytrium.limboauth.command.LimboAuthCommand;
import net.elytrium.limboauth.command.PremiumCommand;
import net.elytrium.limboauth.command.TotpCommand;
import net.elytrium.limboauth.command.UnregisterCommand;
+import net.elytrium.limboauth.dependencies.DatabaseLibrary;
import net.elytrium.limboauth.event.AuthPluginReloadEvent;
import net.elytrium.limboauth.event.PreAuthorizationEvent;
import net.elytrium.limboauth.event.PreEvent;
@@ -158,7 +159,7 @@ public class LimboAuth {
private ScheduledTask purgeCacheTask;
private ScheduledTask purgePremiumCacheTask;
- private JdbcPooledConnectionSource connectionSource;
+ private ConnectionSource connectionSource;
private Dao<RegisteredPlayer, String> playerDao;
private Pattern nicknameValidationPattern;
private Limbo authServer;
@@ -267,29 +268,14 @@ public class LimboAuth {
this.cachedAuthChecks.clear();
Settings.DATABASE dbConfig = Settings.IMP.DATABASE;
- switch (dbConfig.STORAGE_TYPE.toLowerCase(Locale.ROOT)) {
- case "h2": {
- this.connectionSource = new JdbcPooledConnectionSource("jdbc:h2:" + this.dataDirectoryFile.getAbsoluteFile() + "/limboauth");
- break;
- }
- case "mysql": {
- this.connectionSource = new JdbcPooledConnectionSource(
- "jdbc:mysql://" + dbConfig.HOSTNAME + "/" + dbConfig.DATABASE + dbConfig.CONNECTION_PARAMETERS, dbConfig.USER, dbConfig.PASSWORD
- );
- break;
- }
- case "postgresql": {
- this.connectionSource = new JdbcPooledConnectionSource(
- "jdbc:postgresql://" + dbConfig.HOSTNAME + "/" + dbConfig.DATABASE + dbConfig.CONNECTION_PARAMETERS, dbConfig.USER, dbConfig.PASSWORD
- );
- break;
- }
- default: {
- LOGGER.error("Wrong database type.");
- this.server.shutdown();
- return;
- }
- }
+ DatabaseLibrary databaseLibrary = DatabaseLibrary.valueOf(dbConfig.STORAGE_TYPE.toUpperCase(Locale.ROOT));
+ this.connectionSource = databaseLibrary.connectToORM(
+ this.dataDirectoryFile.toPath().toAbsolutePath(),
+ dbConfig.HOSTNAME,
+ dbConfig.DATABASE + dbConfig.CONNECTION_PARAMETERS,
+ dbConfig.USER,
+ dbConfig.PASSWORD
+ );
this.nicknameValidationPattern = Pattern.compile(Settings.IMP.MAIN.ALLOWED_NICKNAME_REGEX);
@@ -789,7 +775,7 @@ public class LimboAuth {
return this.server;
}
- public JdbcPooledConnectionSource getConnectionSource() {
+ public ConnectionSource getConnectionSource() {
return this.connectionSource;
}
@@ -797,17 +783,6 @@ public class LimboAuth {
return this.playerDao;
}
- static {
- // requireNonNull prevents the shade plugin from excluding the drivers in minimized jar.
- Objects.requireNonNull(com.mysql.cj.jdbc.Driver.class);
- Objects.requireNonNull(com.mysql.cj.conf.url.SingleConnectionUrl.class);
-
- Objects.requireNonNull(org.h2.Driver.class);
- Objects.requireNonNull(org.h2.engine.Engine.class);
-
- Objects.requireNonNull(org.postgresql.Driver.class);
- }
-
private static void setLogger(Logger logger) {
LOGGER = logger;
}
diff --git a/src/main/java/net/elytrium/limboauth/Settings.java b/src/main/java/net/elytrium/limboauth/Settings.java
index f8733ad..e258da7 100644
--- a/src/main/java/net/elytrium/limboauth/Settings.java
+++ b/src/main/java/net/elytrium/limboauth/Settings.java
@@ -375,7 +375,7 @@ public class Settings extends YamlConfig {
@Comment("Database settings")
public static class DATABASE {
- @Comment("Database type: mysql, postgresql or h2.")
+ @Comment("Database type: mysql, postgresql, sqlite or h2.")
public String STORAGE_TYPE = "h2";
@Comment("Settings for Network-based database (like MySQL, PostgreSQL): ")
diff --git a/src/main/java/net/elytrium/limboauth/dependencies/BaseLibrary.java b/src/main/java/net/elytrium/limboauth/dependencies/BaseLibrary.java
new file mode 100644
index 0000000..e8c34b5
--- /dev/null
+++ b/src/main/java/net/elytrium/limboauth/dependencies/BaseLibrary.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2021 - 2022 Elytrium
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.elytrium.limboauth.dependencies;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+
+public enum BaseLibrary {
+ H2_V1(
+ "com.h2database",
+ "h2",
+ "1.4.200"
+ ),
+ H2_V2(
+ "com.h2database",
+ "h2",
+ "2.1.214"
+ ),
+ MYSQL(
+ "com.mysql",
+ "mysql-connector-j",
+ "8.0.31"
+ ),
+ POSTGRESQL(
+ "org.postgresql",
+ "postgresql",
+ "42.5.1"
+ ),
+ SQLITE(
+ "org.xerial",
+ "sqlite-jdbc",
+ "3.40.0.0"
+ );
+
+ private final Path filenamePath;
+ private final URL mavenRepoURL;
+
+ BaseLibrary(String groupId, String artifactId, String version) {
+ String mavenPath = String.format("%s/%s/%s/%s-%s.jar",
+ groupId.replace(".", "/"),
+ artifactId,
+ version,
+ artifactId,
+ version
+ );
+
+ this.filenamePath = Path.of("libraries/" + mavenPath);
+
+ URL mavenRepoURL = null;
+ try {
+ mavenRepoURL = new URL("https://repo1.maven.org/maven2/" + mavenPath);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ this.mavenRepoURL = mavenRepoURL;
+ }
+
+ @SuppressFBWarnings("NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE")
+ public URL getClassLoaderURL() throws MalformedURLException {
+ if (!Files.exists(this.filenamePath)) {
+ try {
+ try (InputStream in = this.mavenRepoURL.openStream()) {
+ Files.createDirectories(this.filenamePath.getParent());
+ Files.copy(in, Files.createFile(this.filenamePath), StandardCopyOption.REPLACE_EXISTING);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ return this.filenamePath.toUri().toURL();
+ }
+}
diff --git a/src/main/java/net/elytrium/limboauth/dependencies/DatabaseLibrary.java b/src/main/java/net/elytrium/limboauth/dependencies/DatabaseLibrary.java
new file mode 100644
index 0000000..3a8a070
--- /dev/null
+++ b/src/main/java/net/elytrium/limboauth/dependencies/DatabaseLibrary.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2021 - 2022 Elytrium
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.elytrium.limboauth.dependencies;
+
+import com.j256.ormlite.jdbc.JdbcSingleConnectionSource;
+import com.j256.ormlite.support.ConnectionSource;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.sql.Connection;
+import java.sql.Driver;
+import java.sql.DriverManager;
+import java.sql.Statement;
+import java.util.Properties;
+
+public enum DatabaseLibrary {
+ H2_LEGACY_V1(
+ BaseLibrary.H2_V1,
+ (classLoader, dir, jdbc, user, password) -> fromDriver(classLoader.loadClass("org.h2.Driver"), jdbc, null, null, false),
+ (dir, hostname, database) -> "jdbc:h2:" + dir + "/limboauth"
+ ),
+ H2(
+ BaseLibrary.H2_V2,
+ (classLoader, dir, jdbc, user, password) -> {
+ Connection modernConnection = fromDriver(classLoader.loadClass("org.h2.Driver"), jdbc, null, null, true);
+
+ Path legacyDatabase = dir.resolve("limboauth.mv.db");
+ if (Files.exists(legacyDatabase)) {
+ Path dumpFile = dir.resolve("limboauth.dump.sql");
+ try (Connection legacyConnection = H2_LEGACY_V1.connect(dir, null, null, user, password)) {
+ try (Statement migrateStatement = legacyConnection.createStatement()) {
+ migrateStatement.execute("SCRIPT TO '" + dumpFile + "'");
+ }
+ }
+
+ try (Statement migrateStatement = modernConnection.createStatement()) {
+ migrateStatement.execute("RUNSCRIPT FROM '" + dumpFile + "'");
+ }
+
+ Files.delete(dumpFile);
+ Files.move(legacyDatabase, dir.resolve("limboauth-v1-backup.mv.db"));
+ }
+
+ return modernConnection;
+ },
+ (dir, hostname, database) -> "jdbc:h2:" + dir + "/limboauth-v2"
+ ),
+ MYSQL(
+ BaseLibrary.MYSQL,
+ (classLoader, dir, jdbc, user, password) -> fromDriver(classLoader.loadClass("com.mysql.cj.jdbc.Driver"), jdbc, user, password, true),
+ (dir, hostname, database) ->
+ "jdbc:mysql://" + hostname + "/" + database
+ ),
+ POSTGRESQL(
+ BaseLibrary.POSTGRESQL,
+ (classLoader, dir, jdbc, user, password) -> fromDriver(classLoader.loadClass("org.postgresql.Driver"), jdbc, user, password, true),
+ (dir, hostname, database) -> "jdbc:postgresql://" + hostname + "/" + database
+ ),
+ SQLITE(
+ BaseLibrary.SQLITE,
+ (classLoader, dir, jdbc, user, password) -> fromDriver(classLoader.loadClass("org.sqlite.JDBC"), jdbc, user, password, true),
+ (dir, hostname, database) -> "jdbc:sqlite:" + dir + "/limboauth.db"
+ );
+
+ private final BaseLibrary baseLibrary;
+ private final DatabaseConnector connector;
+ private final DatabaseStringGetter stringGetter;
+
+ DatabaseLibrary(BaseLibrary baseLibrary, DatabaseConnector connector, DatabaseStringGetter stringGetter) {
+ this.baseLibrary = baseLibrary;
+ this.connector = connector;
+ this.stringGetter = stringGetter;
+ }
+
+ public Connection connect(ClassLoader classLoader, Path dir, String hostname, String database, String user, String password) throws Exception {
+ return this.connect(classLoader, dir, this.stringGetter.getJdbcString(dir, hostname, database), user, password);
+ }
+
+ public Connection connect(Path dir, String hostname, String database, String user, String password) throws Exception {
+ return this.connect(dir, this.stringGetter.getJdbcString(dir, hostname, database), user, password);
+ }
+
+ public Connection connect(ClassLoader classLoader, Path dir, String jdbc, String user, String password) throws Exception {
+ return this.connector.connect(classLoader, dir, jdbc, user, password);
+ }
+
+ public Connection connect(Path dir, String jdbc, String user, String password) throws Exception {
+ return this.connector.connect(new IsolatedClassLoader(new URL[]{this.baseLibrary.getClassLoaderURL()}), dir, jdbc, user, password);
+ }
+
+ public ConnectionSource connectToORM(Path dir, String hostname, String database, String user, String password) throws Exception {
+ String jdbc = this.stringGetter.getJdbcString(dir, hostname, database);
+ URL baseLibraryURL = this.baseLibrary.getClassLoaderURL();
+ ClassLoader currentClassLoader = DatabaseLibrary.class.getClassLoader();
+ Method addPath = currentClassLoader.getClass().getDeclaredMethod("addPath", Path.class);
+ addPath.setAccessible(true);
+ addPath.invoke(currentClassLoader, Path.of(baseLibraryURL.toURI()));
+
+ return new JdbcSingleConnectionSource(jdbc, this.connect(currentClassLoader, dir, jdbc, hostname, user, password));
+ }
+
+ private static Connection fromDriver(Class<?> connectionClass, String jdbc, String user, String password, boolean register) throws Exception {
+ Constructor<?> legacyConstructor = connectionClass.getConstructor();
+
+ Properties info = new Properties();
+ if (user != null) {
+ info.put("user", user);
+ }
+
+ if (password != null) {
+ info.put("password", password);
+ }
+
+ Object driver = legacyConstructor.newInstance();
+
+ if (!register) {
+ DriverManager.deregisterDriver((Driver) driver);
+ }
+
+ Method connect = connectionClass.getDeclaredMethod("connect", String.class, Properties.class);
+ connect.setAccessible(true);
+ return (Connection) connect.invoke(driver, jdbc, info);
+ }
+
+ public interface DatabaseConnector {
+ Connection connect(ClassLoader classLoader, Path dir, String jdbc, String user, String password) throws Exception;
+ }
+
+ public interface DatabaseStringGetter {
+ String getJdbcString(Path dir, String hostname, String database);
+ }
+}
diff --git a/src/main/java/net/elytrium/limboauth/dependencies/IsolatedClassLoader.java b/src/main/java/net/elytrium/limboauth/dependencies/IsolatedClassLoader.java
new file mode 100644
index 0000000..ee53ddd
--- /dev/null
+++ b/src/main/java/net/elytrium/limboauth/dependencies/IsolatedClassLoader.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2021 - 2022 Elytrium
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.elytrium.limboauth.dependencies;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+
+public class IsolatedClassLoader extends URLClassLoader {
+
+ public IsolatedClassLoader(URL[] urls) {
+ super(urls, ClassLoader.getSystemClassLoader().getParent());
+ }
+
+ static {
+ ClassLoader.registerAsParallelCapable();
+ }
+}