Skip to content
Merged
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
2 changes: 2 additions & 0 deletions SimpleAPI/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
<include>com/bencodez/simpleapi/sql/mysql/AbstractSqlTable*.class</include>
<include>com/bencodez/simpleapi/sql/mysql/ConnectionManager*.class</include>
<include>com/bencodez/simpleapi/sql/mysql/DbType.class</include>
<include>com/bencodez/simpleapi/sql/mysql/PostgreSqlTlsMode.class</include>
<include>com/bencodez/simpleapi/sql/mysql/MySQL*.class</include>
<include>com/bencodez/simpleapi/sql/mysql/config/MysqlConfig.class</include>
<include>com/bencodez/simpleapi/sql/mysql/config/MysqlConfigView.class</include>
Expand Down Expand Up @@ -117,6 +118,7 @@
<include>com/bencodez/simpleapi/sql/mysql/AbstractSqlTable.java</include>
<include>com/bencodez/simpleapi/sql/mysql/ConnectionManager.java</include>
<include>com/bencodez/simpleapi/sql/mysql/DbType.java</include>
<include>com/bencodez/simpleapi/sql/mysql/PostgreSqlTlsMode.java</include>
<include>com/bencodez/simpleapi/sql/mysql/MySQL.java</include>
<include>com/bencodez/simpleapi/sql/mysql/config/MysqlConfig.java</include>
<include>com/bencodez/simpleapi/sql/mysql/config/MysqlConfigView.java</include>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package com.bencodez.simpleapi.core.sql;

import com.bencodez.simpleapi.sql.mysql.config.MysqlConfig;

import java.util.Objects;

import com.bencodez.simpleapi.file.config.ConfigView;
import com.bencodez.simpleapi.sql.mysql.DbType;
import com.bencodez.simpleapi.sql.mysql.PostgreSqlTlsMode;
import com.bencodez.simpleapi.sql.mysql.config.MysqlConfig;

/** Platform-neutral snapshot of the existing MySQL section keys and defaults. */
public class MysqlConfigView extends MysqlConfig {
Expand All @@ -31,6 +32,7 @@ public MysqlConfigView(ConfigView section) {
: section.getBoolean("UseMariaDB", false) ? DbType.MARIADB : DbType.MYSQL);
setDriver(section.getString("Driver", ""));
setUseSSL(section.getBoolean("UseSSL", false));
setPostgreSqlTlsMode(PostgreSqlTlsMode.fromString(section.getString("PostgreSqlTlsMode", "LEGACY")));
setPublicKeyRetrieval(section.getBoolean("PublicKeyRetrieval", false));
setUseMariaDB(section.getBoolean("UseMariaDB", false));
setLine(section.getString("Line", ""));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package com.bencodez.simpleapi.sql.mysql;

import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Locale;
import java.util.Objects;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
Expand Down Expand Up @@ -81,6 +85,13 @@ public class ConnectionManager {
@Setter
private boolean useSSL = false;

@Getter
private PostgreSqlTlsMode postgreSqlTlsMode = PostgreSqlTlsMode.LEGACY;

public void setPostgreSqlTlsMode(PostgreSqlTlsMode mode) {
postgreSqlTlsMode = Objects.requireNonNull(mode, "mode");
}

/**
* Legacy flag still supported; only used if dbType isn't explicitly set.
*/
Expand Down Expand Up @@ -207,7 +218,7 @@ private String resolveMariaDbWithFallback() throws ClassNotFoundException {
}
}

private String buildJdbcUrl(String driverClassName) {
String buildJdbcUrl(String driverClassName) {
String extra = (str == null ? "" : str);

// Postgres
Expand All @@ -216,10 +227,17 @@ private String buildJdbcUrl(String driverClassName) {

// Defaults:
// - reWriteBatchedInserts improves batch perf
// - sslmode if useSSL
// - absent explicit mode preserves the legacy UseSSL behavior
String defaults = "reWriteBatchedInserts=true";
if (useSSL) {
if (postgreSqlTlsMode == PostgreSqlTlsMode.LEGACY && useSSL) {
defaults += "&sslmode=require";
} else if (postgreSqlTlsMode != PostgreSqlTlsMode.LEGACY) {
rejectConflictingPostgreSqlTlsOptions(extra);
defaults += "&sslmode=" + postgreSqlTlsMode.getJdbcValue();
if (postgreSqlTlsMode == PostgreSqlTlsMode.REQUIRE
|| postgreSqlTlsMode == PostgreSqlTlsMode.VERIFY_FULL) {
defaults += "&gssEncMode=disable";
}
}

if (extra.isEmpty()) {
Expand All @@ -240,6 +258,9 @@ private String buildJdbcUrl(String driverClassName) {
}

// MySQL / MariaDB
if (postgreSqlTlsMode != PostgreSqlTlsMode.LEGACY) {
throw new IllegalArgumentException("PostgreSqlTlsMode applies only to PostgreSQL connections");
}
boolean maria = (dbType == DbType.MARIADB) || "org.mariadb.jdbc.Driver".equals(driverClassName);
String base = maria ? String.format("jdbc:mariadb://%s:%s/%s", host, port, database)
: String.format("jdbc:mysql://%s:%s/%s", host, port, database);
Expand All @@ -250,6 +271,24 @@ private String buildJdbcUrl(String driverClassName) {
+ extra;
}

private void rejectConflictingPostgreSqlTlsOptions(String extra) {
String options = extra.startsWith("?") || extra.startsWith("&") ? extra.substring(1) : extra;
for (String parameter : options.split("&", -1)) {
String rawKey = parameter.split("=", 2)[0];
String key;
try {
key = URLDecoder.decode(rawKey, StandardCharsets.UTF_8).trim().toLowerCase(Locale.ROOT);
} catch (IllegalArgumentException ex) {
throw new IllegalArgumentException("Invalid encoded parameter name in PostgreSQL Line", ex);
}
if (key.equals("sslmode") || key.equals("ssl") || key.equals("sslfactory")
|| key.equals("sslhostnameverifier")
|| (postgreSqlTlsMode != PostgreSqlTlsMode.DISABLE && key.equals("gssencmode"))) {
throw new IllegalArgumentException("PostgreSqlTlsMode conflicts with a TLS parameter in Line");
}
}
}

// --- Pool Configuration ---

public boolean open() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public boolean connect(MysqlConfig config) {
} else {
connectionManager.setDbType(config.isUseMariaDB() ? DbType.MARIADB : DbType.MYSQL);
}
connectionManager.setPostgreSqlTlsMode(config.getPostgreSqlTlsMode());

String driver = config.getDriver();
if (driver != null && !driver.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.bencodez.simpleapi.sql.mysql;

import java.util.Locale;

/** PostgreSQL JDBC TLS policy. LEGACY preserves the existing UseSSL setting. */
public enum PostgreSqlTlsMode {
LEGACY(null),
DISABLE("disable"),
REQUIRE("require"),
VERIFY_FULL("verify-full");

private final String jdbcValue;

PostgreSqlTlsMode(String jdbcValue) {
this.jdbcValue = jdbcValue;
}

public String getJdbcValue() {
return jdbcValue;
}

public static PostgreSqlTlsMode fromString(String raw) {
if (raw == null || raw.trim().isEmpty()) {
return LEGACY;
}
String normalized = raw.trim().replace('-', '_').toUpperCase(Locale.ROOT);
try {
return valueOf(normalized);
} catch (IllegalArgumentException ex) {
throw new IllegalArgumentException("Invalid PostgreSqlTlsMode; expected LEGACY, DISABLE, REQUIRE, or VERIFY_FULL", ex);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package com.bencodez.simpleapi.sql.mysql.config;

import java.util.Objects;

import com.bencodez.simpleapi.sql.mysql.DbType;
import com.bencodez.simpleapi.sql.mysql.PostgreSqlTlsMode;

import lombok.Getter;
import lombok.Setter;
Expand Down Expand Up @@ -82,6 +85,13 @@ public class MysqlConfig {
@Setter
private boolean useSSL;

@Getter
private PostgreSqlTlsMode postgreSqlTlsMode = PostgreSqlTlsMode.LEGACY;

public void setPostgreSqlTlsMode(PostgreSqlTlsMode mode) {
postgreSqlTlsMode = Objects.requireNonNull(mode, "mode");
}

@Getter
@Setter
private boolean publicKeyRetrieval;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.bencodez.simpleapi.sql.mysql.config;

import com.bencodez.simpleapi.sql.mysql.DbType;
import com.bencodez.simpleapi.sql.mysql.PostgreSqlTlsMode;

import net.md_5.bungee.config.Configuration;

Expand Down Expand Up @@ -50,6 +51,7 @@ public MysqlConfigBungee(Configuration section) {

// --- Driver / Behavior Options ---
setUseSSL(section.getBoolean("UseSSL", false));
setPostgreSqlTlsMode(PostgreSqlTlsMode.fromString(section.getString("PostgreSqlTlsMode", "LEGACY")));
setPublicKeyRetrieval(section.getBoolean("PublicKeyRetrieval", false));
setUseMariaDB(section.getBoolean("UseMariaDB", false));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import org.bukkit.configuration.ConfigurationSection;

import com.bencodez.simpleapi.sql.mysql.DbType;
import com.bencodez.simpleapi.sql.mysql.PostgreSqlTlsMode;

public class MysqlConfigSpigot extends MysqlConfig {

Expand Down Expand Up @@ -49,6 +50,7 @@ public MysqlConfigSpigot(ConfigurationSection section) {

// --- Driver / Behavior Options ---
setUseSSL(section.getBoolean("UseSSL", false));
setPostgreSqlTlsMode(PostgreSqlTlsMode.fromString(section.getString("PostgreSqlTlsMode", "LEGACY")));
setPublicKeyRetrieval(section.getBoolean("PublicKeyRetrieval", false));
setUseMariaDB(section.getBoolean("UseMariaDB", false));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import com.bencodez.simpleapi.file.velocity.VelocityYMLFile;
import com.bencodez.simpleapi.sql.mysql.DbType;
import com.bencodez.simpleapi.sql.mysql.PostgreSqlTlsMode;

public class MysqlConfigVelocity extends MysqlConfig {

Expand Down Expand Up @@ -59,6 +60,8 @@ private void load(String prePath, VelocityYMLFile config) {

// --- Driver / Behavior Options ---
setUseSSL(config.getBoolean(node(config, prePath, "UseSSL"), false));
setPostgreSqlTlsMode(PostgreSqlTlsMode.fromString(
config.getString(node(config, prePath, "PostgreSqlTlsMode"), "LEGACY")));
setPublicKeyRetrieval(config.getBoolean(node(config, prePath, "PublicKeyRetrieval"), false));
setUseMariaDB(config.getBoolean(node(config, prePath, "UseMariaDB"), false));

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.bencodez.simpleapi.sql.mysql;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.nio.file.Path;

import org.bukkit.configuration.MemoryConfiguration;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.spongepowered.configurate.BasicConfigurationNode;

import com.bencodez.simpleapi.file.config.configurate.ConfigurateConfigView;
import com.bencodez.simpleapi.file.velocity.VelocityYMLFile;
import com.bencodez.simpleapi.sql.mysql.config.MysqlConfigBungee;
import com.bencodez.simpleapi.sql.mysql.config.MysqlConfigSpigot;
import com.bencodez.simpleapi.sql.mysql.config.MysqlConfigVelocity;
import com.bencodez.simpleapi.sql.mysql.config.MysqlConfigView;

import net.md_5.bungee.config.Configuration;

class PostgreSqlTlsConfigTest {
@TempDir Path tempDir;

@Test void allAdaptersReadExplicitModeAndPreserveLegacyDefault() {
MemoryConfiguration spigot = new MemoryConfiguration();
Configuration bungee = new Configuration();
var shared = BasicConfigurationNode.root();
VelocityYMLFile velocity = new VelocityYMLFile(tempDir.resolve("mysql.yml").toFile());
assertEquals(PostgreSqlTlsMode.LEGACY, new MysqlConfigSpigot(spigot).getPostgreSqlTlsMode());
assertEquals(PostgreSqlTlsMode.LEGACY, new MysqlConfigBungee(bungee).getPostgreSqlTlsMode());
assertEquals(PostgreSqlTlsMode.LEGACY,
new MysqlConfigView(new ConfigurateConfigView(shared)).getPostgreSqlTlsMode());
assertEquals(PostgreSqlTlsMode.LEGACY, new MysqlConfigVelocity(velocity).getPostgreSqlTlsMode());

spigot.set("PostgreSqlTlsMode", "verify-full");
bungee.set("PostgreSqlTlsMode", "verify-full");
shared.node("PostgreSqlTlsMode").raw("verify-full");
velocity.getNode("MySQL", "PostgreSqlTlsMode").raw("verify-full");
assertEquals(PostgreSqlTlsMode.VERIFY_FULL, new MysqlConfigSpigot(spigot).getPostgreSqlTlsMode());
assertEquals(PostgreSqlTlsMode.VERIFY_FULL, new MysqlConfigBungee(bungee).getPostgreSqlTlsMode());
assertEquals(PostgreSqlTlsMode.VERIFY_FULL,
new MysqlConfigView(new ConfigurateConfigView(shared)).getPostgreSqlTlsMode());
assertEquals(PostgreSqlTlsMode.VERIFY_FULL,
new MysqlConfigVelocity("MySQL", velocity).getPostgreSqlTlsMode());
}

@Test void invalidModeFailsAtConfigLoad() {
MemoryConfiguration spigot = new MemoryConfiguration();
spigot.set("PostgreSqlTlsMode", "anything");
assertThrows(IllegalArgumentException.class, () -> new MysqlConfigSpigot(spigot));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package com.bencodez.simpleapi.sql.mysql;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

import org.junit.jupiter.api.Test;

class PostgreSqlTlsModeTest {
private ConnectionManager postgres() {
ConnectionManager manager = new ConnectionManager("db.example.test", "5432", "user", "password", "votes");
manager.setDbType(DbType.POSTGRESQL);
return manager;
}

@Test void legacyModePreservesBothUseSslSettingsAndRawLine() {
ConnectionManager manager = postgres();
assertEquals("jdbc:postgresql://db.example.test:5432/votes?reWriteBatchedInserts=true",
manager.buildJdbcUrl("org.postgresql.Driver"));
manager.setUseSSL(true);
assertEquals("jdbc:postgresql://db.example.test:5432/votes?reWriteBatchedInserts=true&sslmode=require",
manager.buildJdbcUrl("org.postgresql.Driver"));
manager.setStr("?sslmode=disable");
assertEquals("jdbc:postgresql://db.example.test:5432/votes?sslmode=disable&reWriteBatchedInserts=true&sslmode=require",
manager.buildJdbcUrl("org.postgresql.Driver"));
}

@Test void explicitModeControlsPostgresTlsRegardlessOfUseSsl() {
ConnectionManager manager = postgres();
manager.setPostgreSqlTlsMode(PostgreSqlTlsMode.VERIFY_FULL);
manager.setStr("?sslrootcert=/etc/db/ca.pem&applicationName=votes");
String verified = "jdbc:postgresql://db.example.test:5432/votes?sslrootcert=/etc/db/ca.pem"
+ "&applicationName=votes&reWriteBatchedInserts=true&sslmode=verify-full&gssEncMode=disable";
assertEquals(verified, manager.buildJdbcUrl("org.postgresql.Driver"));
manager.setUseSSL(true);
assertEquals(verified, manager.buildJdbcUrl("org.postgresql.Driver"));
manager.setPostgreSqlTlsMode(PostgreSqlTlsMode.REQUIRE);
manager.setStr("&applicationName=votes");
assertEquals("jdbc:postgresql://db.example.test:5432/votes?reWriteBatchedInserts=true&sslmode=require&gssEncMode=disable&applicationName=votes",
manager.buildJdbcUrl("org.postgresql.Driver"));
manager.setPostgreSqlTlsMode(PostgreSqlTlsMode.DISABLE);
manager.setStr("");
assertEquals("jdbc:postgresql://db.example.test:5432/votes?reWriteBatchedInserts=true&sslmode=disable",
manager.buildJdbcUrl("org.postgresql.Driver"));
}

@Test void explicitModeRejectsConflictingRawLineOptions() {
ConnectionManager manager = postgres();
manager.setPostgreSqlTlsMode(PostgreSqlTlsMode.VERIFY_FULL);
for (String line : new String[] {"?sslmode=disable", "&SSLMODE=require", "ssl%6dode=disable",
"?applicationName=votes&ssl=false", "&sslfactory=com.example.TrustAll",
"sslhostnameverifier=com.example.TrustAll", "gssEncMode=prefer"}) {
manager.setStr(line);
assertThrows(IllegalArgumentException.class, () -> manager.buildJdbcUrl("org.postgresql.Driver"), line);
}
manager.setStr("?ssl%zz=disable");
assertThrows(IllegalArgumentException.class, () -> manager.buildJdbcUrl("org.postgresql.Driver"));
}

@Test void mysqlAndMariaDbKeepLegacyUrlsAndRejectPostgresModes() {
ConnectionManager manager = postgres();
manager.setDbType(DbType.MYSQL);
String mysqlUrl = manager.buildJdbcUrl("com.mysql.cj.jdbc.Driver");
assertEquals("jdbc:mysql://db.example.test:5432/votes?useSSL=false&allowMultiQueries=true"
+ "&rewriteBatchedStatements=true&useDynamicCharsetInfo=false&allowPublicKeyRetrieval=false"
+ "&tcpKeepAlive=true&connectTimeout=10000&socketTimeout=30000&serverTimezone=UTC", mysqlUrl);
manager.setPostgreSqlTlsMode(PostgreSqlTlsMode.VERIFY_FULL);
assertThrows(IllegalArgumentException.class, () -> manager.buildJdbcUrl("com.mysql.cj.jdbc.Driver"));
manager.setDbType(DbType.MARIADB);
assertThrows(IllegalArgumentException.class, () -> manager.buildJdbcUrl("org.mariadb.jdbc.Driver"));
manager.setPostgreSqlTlsMode(PostgreSqlTlsMode.LEGACY);
assertEquals(mysqlUrl.replace("jdbc:mysql:", "jdbc:mariadb:"),
manager.buildJdbcUrl("org.mariadb.jdbc.Driver"));
}

@Test void parsesModeNamesAndRejectsUnknownValues() {
assertEquals(PostgreSqlTlsMode.LEGACY, PostgreSqlTlsMode.fromString(null));
assertEquals(PostgreSqlTlsMode.LEGACY, PostgreSqlTlsMode.fromString(" "));
assertEquals(PostgreSqlTlsMode.VERIFY_FULL, PostgreSqlTlsMode.fromString(" verify-full "));
assertEquals(PostgreSqlTlsMode.VERIFY_FULL, PostgreSqlTlsMode.fromString("verify_full"));
assertThrows(IllegalArgumentException.class, () -> PostgreSqlTlsMode.fromString("verify-ca"));
}
}
Loading
Loading