Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,10 @@ Install the plugin on both platforms, that is proxy (BungeeCord or Velocity) and
4. Activate ip forwarding in your proxy config
5. Check your database settings in the config of FastLogin on your proxy
* The proxies only ship with a limited set of drivers where Spigot supports more. Therefore, these are supported:
* BungeeCord: `com.mysql.jdbc.Driver` for MySQL/MariaDB
* Velocity: `fastlogin.mariadb.jdbc.Driver` for MySQL/MariaDB
* BungeeCord: `com.mysql.jdbc.Driver` for MySQL/MariaDB/PostgreSQL
* Velocity: `fastlogin.mariadb.jdbc.Driver` for MySQL/MariaDB/PostgreSQL
* Note the embedded file storage SQLite is not available
* MySQL/MariaDB requires an external database server running. Check your server provider if there is one available
* MySQL/MariaDB/PostgreSQL requires an external database server running. Check your server provider if there is one available
or install one.
6. Set proxy and Spigot in offline mode by setting the value `onlinemode` in your `config.yml` to false
7. You should *always* configure the firewall for your Spigot server so that it's only accessible through your proxy
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import com.github.games647.fastlogin.core.hooks.DefaultPasswordGenerator;
import com.github.games647.fastlogin.core.hooks.PasswordGenerator;
import com.github.games647.fastlogin.core.storage.PostgreSQLStorage;
import com.github.games647.fastlogin.core.storage.MySQLStorage;
import com.github.games647.fastlogin.core.storage.SQLStorage;
import com.github.games647.fastlogin.core.storage.SQLiteStorage;
Expand Down Expand Up @@ -230,6 +231,24 @@ public boolean setupDatabase() {

if (type.contains("sqlite")) {
storage = new SQLiteStorage(plugin, database, databaseConfig);
} else if (type.contains("postgresql")) {
String host = config.get("host", "");
int port = config.get("port", 3306);
boolean useSSL = config.get("useSSL", false);

if (useSSL) {
boolean publicKeyRetrieval = config.getBoolean("allowPublicKeyRetrieval", false);
String rsaPublicKeyFile = config.getString("ServerRSAPublicKeyFile");
String sslMode = config.getString("sslMode", "Required");

databaseConfig.addDataSourceProperty("allowPublicKeyRetrieval", publicKeyRetrieval);
databaseConfig.addDataSourceProperty("serverRSAPublicKeyFile", rsaPublicKeyFile);
databaseConfig.addDataSourceProperty("sslMode", sslMode);
}

databaseConfig.setUsername(config.get("username", ""));
databaseConfig.setPassword(config.getString("password"));
storage = new PostgreSQLStorage(plugin, type, host, port, database, databaseConfig, useSSL);
} else {
String host = config.get("host", "");
int port = config.get("port", 3306);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2023 games647 and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.core.storage;

import com.github.games647.fastlogin.core.shared.PlatformPlugin;
import com.zaxxer.hikari.HikariConfig;

public class PostgreSQLStorage extends SQLStorage {

private static final String JDBC_PROTOCOL = "jdbc:";

public PostgreSQLStorage(PlatformPlugin<?> plugin, String driver, String host, int port, String database,
HikariConfig config, boolean useSSL) {
super(plugin.getLog(), plugin.getName(), plugin.getThreadFactory(),
setParams(config, driver, host, port, database, useSSL));
}

private static HikariConfig setParams(HikariConfig config,
String driver, String host, int port, String database,
boolean useSSL) {
// Require SSL on the server if requested in config - this will also verify certificate
// Those values are deprecated in favor of sslMode
config.addDataSourceProperty("useSSL", useSSL);
config.addDataSourceProperty("requireSSL", useSSL);

// adding paranoid, hides hostname, username, version and so
// could be useful for hiding server details
config.addDataSourceProperty("paranoid", true);

config.setJdbcUrl(JDBC_PROTOCOL + buildJDBCUrl(driver, host, port, database));

return config;
}

private static String buildJDBCUrl(String driver, String host, int port, String database) {
return "postgresql://" + host + ':' + port + '/' + database;
}

@Override
protected String getCreateTableStmt() {
// PostgreSQL has a different syntax for id column
return CREATE_TABLE_STMT
.replace("`", "\"")
.replace("INTEGER PRIMARY KEY AUTO_INCREMENT", "SERIAL PRIMARY KEY");
}

@Override
protected String getAddFloodgateColumnStmt() {
// PostgreSQL has a different syntax
return ADD_FLOODGATE_COLUMN_STMT
.replace("`", "\"")
.replace("INTEGER(3)", "INTEGER");
}

@Override
protected String getLoadByNameStmt() {
return LOAD_BY_NAME_STMT
.replace("`", "\"");
}

@Override
protected String getLoadByUuidStmt() {
return LOAD_BY_UUID_STMT
.replace("`", "\"");
}

@Override
protected String getInsertProfileStmt() {
return INSERT_PROFILE_STMT
.replace("`", "\"");
}

@Override
protected String getUpdateProfileStmt() {
return UPDATE_PROFILE_STMT
.replace("`", "\"");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,14 @@ public abstract class SQLStorage implements AuthStorage {
protected static final String ADD_FLOODGATE_COLUMN_STMT = "ALTER TABLE `" + PREMIUM_TABLE
+ "` ADD COLUMN `Floodgate` INTEGER(3)";

protected static final String LOAD_BY_NAME = "SELECT * FROM `" + PREMIUM_TABLE
protected static final String LOAD_BY_NAME_STMT = "SELECT * FROM `" + PREMIUM_TABLE
+ "` WHERE `Name`=? LIMIT 1";
protected static final String LOAD_BY_UUID = "SELECT * FROM `" + PREMIUM_TABLE
protected static final String LOAD_BY_UUID_STMT = "SELECT * FROM `" + PREMIUM_TABLE
+ "` WHERE `UUID`=? LIMIT 1";
protected static final String INSERT_PROFILE = "INSERT INTO `" + PREMIUM_TABLE
protected static final String INSERT_PROFILE_STMT = "INSERT INTO `" + PREMIUM_TABLE
+ "` (`UUID`, `Name`, `Premium`, `Floodgate`, `LastIp`) " + "VALUES (?, ?, ?, ?, ?) ";
// limit not necessary here, because it's unique
protected static final String UPDATE_PROFILE = "UPDATE `" + PREMIUM_TABLE
protected static final String UPDATE_PROFILE_STMT = "UPDATE `" + PREMIUM_TABLE
+ "` SET `UUID`=?, `Name`=?, `Premium`=?, `Floodgate`=?, `LastIp`=?, "
+ "`LastLogin`=CURRENT_TIMESTAMP WHERE `UserID`=?";

Expand Down Expand Up @@ -97,7 +97,7 @@ public void createTables() throws SQLException {
// add Floodgate column
DatabaseMetaData md = con.getMetaData();
if (isColumnMissing(md, "Floodgate")) {
stmt.executeUpdate(ADD_FLOODGATE_COLUMN_STMT);
stmt.executeUpdate(getAddFloodgateColumnStmt());
}

}
Expand All @@ -112,7 +112,7 @@ private boolean isColumnMissing(DatabaseMetaData metaData, String columnName) th
@Override
public StoredProfile loadProfile(String name) {
try (Connection con = dataSource.getConnection();
PreparedStatement loadStmt = con.prepareStatement(LOAD_BY_NAME)
PreparedStatement loadStmt = con.prepareStatement(getLoadByNameStmt())
) {
loadStmt.setString(1, name);

Expand All @@ -130,7 +130,7 @@ public StoredProfile loadProfile(String name) {
@Override
public StoredProfile loadProfile(UUID uuid) {
try (Connection con = dataSource.getConnection();
PreparedStatement loadStmt = con.prepareStatement(LOAD_BY_UUID)) {
PreparedStatement loadStmt = con.prepareStatement(getLoadByUuidStmt())) {
loadStmt.setString(1, UUIDAdapter.toMojangId(uuid));

try (ResultSet resultSet = loadStmt.executeQuery()) {
Expand All @@ -147,7 +147,10 @@ private Optional<StoredProfile> parseResult(ResultSet resultSet) throws SQLExcep
if (resultSet.next()) {
long userId = resultSet.getInt("UserID");

UUID uuid = Optional.ofNullable(resultSet.getString("UUID")).map(UUIDAdapter::parseId).orElse(null);
UUID uuid = Optional.ofNullable(resultSet.getString("UUID"))
.map(String::trim)
.map(UUIDAdapter::parseId)
.orElse(null);

String name = resultSet.getString("Name");
boolean premium = resultSet.getBoolean("Premium");
Expand Down Expand Up @@ -177,7 +180,7 @@ public void save(StoredProfile playerProfile) {
playerProfile.getSaveLock().lock();
try {
if (playerProfile.isSaved()) {
try (PreparedStatement saveStmt = con.prepareStatement(UPDATE_PROFILE)) {
try (PreparedStatement saveStmt = con.prepareStatement(getUpdateProfileStmt())) {
saveStmt.setString(1, uuid);
saveStmt.setString(2, playerProfile.getName());
saveStmt.setBoolean(3, playerProfile.isPremium());
Expand All @@ -188,7 +191,8 @@ public void save(StoredProfile playerProfile) {
saveStmt.execute();
}
} else {
try (PreparedStatement saveStmt = con.prepareStatement(INSERT_PROFILE, RETURN_GENERATED_KEYS)) {
try (PreparedStatement saveStmt = con.prepareStatement(getInsertProfileStmt(),
RETURN_GENERATED_KEYS)) {
saveStmt.setString(1, uuid);

saveStmt.setString(2, playerProfile.getName());
Expand All @@ -214,13 +218,37 @@ public void save(StoredProfile playerProfile) {
}

/**
* SQLite has a slightly different syntax, so this will be overridden by SQLiteStorage
* SQLite and PostgreSQL have a slightly different syntax, so this will be overridden by SQLiteStorage and so on...
* @return An SQL Statement to create the `premium` table
*/
protected String getCreateTableStmt() {
return CREATE_TABLE_STMT;
}

/**
* PostgreSQL has a slightly different syntax, so this will be overridden by PostgreSQLStorage
* @return An SQL Statement to create the `premium` table
*/
protected String getAddFloodgateColumnStmt() {
return ADD_FLOODGATE_COLUMN_STMT;
}

protected String getLoadByNameStmt() {
return LOAD_BY_NAME_STMT;
}

protected String getLoadByUuidStmt() {
return LOAD_BY_UUID_STMT;
}

protected String getInsertProfileStmt() {
return INSERT_PROFILE_STMT;
}

protected String getUpdateProfileStmt() {
return UPDATE_PROFILE_STMT;
}

@Override
public void close() {
dataSource.close();
Expand Down
9 changes: 9 additions & 0 deletions core/src/main/resources/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,15 @@ database: '{pluginDir}/FastLogin.db'
#username: 'myUser'
#password: 'myPassword'

# PostgreSQL
# If you want to enable it, uncomment only the lines below; this not this line.
#driver: 'postgresql'
#host: '127.0.0.1'
#port: 5432
#database: 'fastlogin'
#username: 'myUser'
#password: 'myPassword'

# Advanced Connection Pool settings in seconds
#timeout: 30
#lifetime: 30
Expand Down