From 2eb994f313a6c25426e0352ca927ecc3d39b19e6 Mon Sep 17 00:00:00 2001 From: Zaxar163 Date: Tue, 13 Aug 2019 15:10:23 +0300 Subject: [PATCH] =?UTF-8?q?[FEATURE]=20=D0=9F=D0=BE=D0=B4=D0=B4=D0=B5?= =?UTF-8?q?=D1=80=D0=B6=D0=BA=D0=B0=20PostgreSQL!?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/PostgreSQLSourceConfig.java | 81 ++++++++++++ .../auth/handler/AuthHandler.java | 1 + .../auth/handler/PostgreSQLAuthHandler.java | 116 ++++++++++++++++++ .../auth/provider/AuthProvider.java | 1 + .../auth/provider/PostgreSQLAuthProvider.java | 42 +++++++ 5 files changed, 241 insertions(+) create mode 100644 LaunchServer/src/main/java/pro/gravit/launchserver/auth/PostgreSQLSourceConfig.java create mode 100644 LaunchServer/src/main/java/pro/gravit/launchserver/auth/handler/PostgreSQLAuthHandler.java create mode 100644 LaunchServer/src/main/java/pro/gravit/launchserver/auth/provider/PostgreSQLAuthProvider.java diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/PostgreSQLSourceConfig.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/PostgreSQLSourceConfig.java new file mode 100644 index 00000000..7a852f03 --- /dev/null +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/PostgreSQLSourceConfig.java @@ -0,0 +1,81 @@ +package pro.gravit.launchserver.auth; + +import com.zaxxer.hikari.HikariDataSource; + +import pro.gravit.utils.helper.LogHelper; +import pro.gravit.utils.helper.VerifyHelper; + +import org.postgresql.ds.PGSimpleDataSource; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; + +public final class PostgreSQLSourceConfig implements AutoCloseable { + 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"); + private static final int MAX_POOL_SIZE = VerifyHelper.verifyInt( + Integer.parseUnsignedInt(System.getProperty("launcher.postgresql.maxPoolSize", Integer.toString(3))), + VerifyHelper.POSITIVE, "launcher.postgresql.maxPoolSize can't be <= 0"); + + // Instance + private String poolName; + + // Config + private String address; + private int port; + private String username; + private String password; + private String database; + + // Cache + private DataSource source; + private boolean hikari; + + @Override + public synchronized void close() { + if (hikari) { // Shutdown hikari pool + ((HikariDataSource) source).close(); + } + } + + public synchronized Connection getConnection() throws SQLException { + if (source == null) { // New data source + PGSimpleDataSource postgresqlSource = new PGSimpleDataSource(); + + // Set credentials + postgresqlSource.setServerName(address); + postgresqlSource.setPortNumber(port); + postgresqlSource.setUser(username); + postgresqlSource.setPassword(password); + postgresqlSource.setDatabaseName(database); + + // Try using HikariCP + source = postgresqlSource; + + //noinspection Duplicates + 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(); + } +} \ No newline at end of file diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/handler/AuthHandler.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/handler/AuthHandler.java index 954b65ba..f520cd2f 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/handler/AuthHandler.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/handler/AuthHandler.java @@ -24,6 +24,7 @@ public static void registerHandlers() { providers.register("json", JsonAuthHandler.class); providers.register("memory", MemoryAuthHandler.class); providers.register("mysql", MySQLAuthHandler.class); + providers.register("postgresql", PostgreSQLAuthHandler.class); providers.register("request", RequestAuthHandler.class); providers.register("hibernate", HibernateAuthHandler.class); registredHandl = true; diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/handler/PostgreSQLAuthHandler.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/handler/PostgreSQLAuthHandler.java new file mode 100644 index 00000000..697ee53b --- /dev/null +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/handler/PostgreSQLAuthHandler.java @@ -0,0 +1,116 @@ +package pro.gravit.launchserver.auth.handler; + +import org.postgresql.util.PGobject; + +import pro.gravit.launchserver.auth.PostgreSQLSourceConfig; + +import java.io.IOException; +import java.sql.*; +import java.util.UUID; + +public final class PostgreSQLAuthHandler extends CachedAuthHandler { + private PostgreSQLSourceConfig postgreSQLHolder; + private String uuidColumn; + private String usernameColumn; + private String accessTokenColumn; + private String serverIDColumn; + + + private String queryByUUIDSQL; + private String queryByUsernameSQL; + private String updateAuthSQL; + private String updateServerIDSQL; + + @Override + public void close() { + postgreSQLHolder.close(); + } + + 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; + } + + @Override + protected Entry fetchEntry(String username) throws IOException { + return query(queryByUsernameSQL, username); + } + + @Override + protected Entry fetchEntry(UUID uuid) throws IOException { + return query(queryByUUIDSQL, uuid); + } + + @Override + protected boolean updateAuth(UUID uuid, String username, String accessToken) throws IOException { + try (Connection c = postgreSQLHolder.getConnection(); + PreparedStatement s = c.prepareStatement(updateAuthSQL)) { + s.setString(1, username); // Username case + s.setString(2, accessToken); + + PGobject uuidObject = new PGobject(); + uuidObject.setType("uuid"); + uuidObject.setValue(uuid.toString()); + s.setObject(3, uuidObject); + + // Execute update + s.setQueryTimeout(PostgreSQLSourceConfig.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 = postgreSQLHolder.getConnection(); + PreparedStatement s = c.prepareStatement(updateServerIDSQL)) { + s.setString(1, serverID); + + PGobject uuidObject = new PGobject(); + uuidObject.setType("uuid"); + uuidObject.setValue(uuid.toString()); + s.setObject(2, uuidObject); + + // Execute update + s.setQueryTimeout(PostgreSQLSourceConfig.TIMEOUT); + return s.executeUpdate() > 0; + } catch (SQLException e) { + throw new IOException(e); + } + } + + private Entry query(String sql, String value) throws IOException { + try (Connection c = postgreSQLHolder.getConnection(); + PreparedStatement s = c.prepareStatement(sql)) { + s.setString(1, value); + + // Execute query + s.setQueryTimeout(PostgreSQLSourceConfig.TIMEOUT); + try (ResultSet set = s.executeQuery()) { + return constructEntry(set); + } + } catch (SQLException e) { + throw new IOException(e); + } + } + + private Entry query(String sql, UUID value) throws IOException { + try (Connection c = postgreSQLHolder.getConnection(); + PreparedStatement s = c.prepareStatement(sql)) { + PGobject uuidObject = new PGobject(); + uuidObject.setType("uuid"); + uuidObject.setValue(value.toString()); + + s.setObject(1, uuidObject); + + // Execute query + s.setQueryTimeout(PostgreSQLSourceConfig.TIMEOUT); + try (ResultSet set = s.executeQuery()) { + return constructEntry(set); + } + } catch (SQLException e) { + throw new IOException(e); + } + } +} \ No newline at end of file diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/provider/AuthProvider.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/provider/AuthProvider.java index af84b95d..c3766dcf 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/provider/AuthProvider.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/provider/AuthProvider.java @@ -20,6 +20,7 @@ public static void registerProviders() { providers.register("accept", AcceptAuthProvider.class); providers.register("reject", RejectAuthProvider.class); providers.register("mysql", MySQLAuthProvider.class); + providers.register("postgresql", PostgreSQLAuthProvider.class); providers.register("request", RequestAuthProvider.class); providers.register("json", JsonAuthProvider.class); providers.register("hibernate", HibernateAuthProvider.class); diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/provider/PostgreSQLAuthProvider.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/provider/PostgreSQLAuthProvider.java new file mode 100644 index 00000000..a20a2173 --- /dev/null +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/provider/PostgreSQLAuthProvider.java @@ -0,0 +1,42 @@ +package pro.gravit.launchserver.auth.provider; + + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +import pro.gravit.launcher.ClientPermissions; +import pro.gravit.launchserver.auth.AuthException; +import pro.gravit.launchserver.auth.PostgreSQLSourceConfig; +import pro.gravit.utils.helper.CommonHelper; +import pro.gravit.utils.helper.SecurityHelper; + +public final class PostgreSQLAuthProvider extends AuthProvider { + private PostgreSQLSourceConfig postgreSQLHolder; + private String query; + private String message; + private String[] queryParams; + private boolean usePermission; + + @Override + public AuthProviderResult auth(String login, String password, String ip) throws SQLException, AuthException { + try (Connection c = postgreSQLHolder.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(PostgreSQLSourceConfig.TIMEOUT); + try (ResultSet set = s.executeQuery()) { + return set.next() ? new AuthProviderResult(set.getString(1), SecurityHelper.randomStringToken(), usePermission ? new ClientPermissions(set.getLong(2)) : srv.config.permissionsHandler.getPermissions(set.getString(1))) : authError(message); + } + } + } + + @Override + public void close() { + // Do nothing + } +} \ No newline at end of file