From 187b9d64fb10a4e72ca2f9e1c9acf6ea57d94987 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 2 Dec 2022 15:07:02 -0300 Subject: [PATCH] configure dedicated db user for database migrations: DML-only user for datasource, but DDL user for migration #24639 --- .../FlywayExtensionDifferentUsernameTest.java | 46 ++++++++++++ .../config-for-different-username.properties | 4 ++ .../flyway/runtime/DataSourceDecorator.java | 72 +++++++++++++++++++ .../quarkus/flyway/runtime/FlywayCreator.java | 8 ++- .../FlywayDataSourceRuntimeConfig.java | 20 ++++++ 5 files changed, 149 insertions(+), 1 deletion(-) create mode 100755 extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionDifferentUsernameTest.java create mode 100755 extensions/flyway/deployment/src/test/resources/config-for-different-username.properties create mode 100644 extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/DataSourceDecorator.java mode change 100644 => 100755 extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayCreator.java mode change 100644 => 100755 extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayDataSourceRuntimeConfig.java diff --git a/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionDifferentUsernameTest.java b/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionDifferentUsernameTest.java new file mode 100755 index 0000000000000..515daf81998b0 --- /dev/null +++ b/extensions/flyway/deployment/src/test/java/io/quarkus/flyway/test/FlywayExtensionDifferentUsernameTest.java @@ -0,0 +1,46 @@ +package io.quarkus.flyway.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +import javax.inject.Inject; +import javax.sql.DataSource; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; + +public class FlywayExtensionDifferentUsernameTest { + + @Inject + DataSource datasource; + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + + .addAsResource("config-for-different-username.properties", "application.properties")); + + @Test + @DisplayName("Check if connected with new username") + public void testFlywayInitSql() throws SQLException { + + int var = 0; + try (Connection con = datasource.getConnection(); + PreparedStatement ps = con.prepareStatement("SELECT 100"); + ResultSet rs = ps.executeQuery()) { + if (rs.next()) { + var = rs.getInt(1); + } + } + assertEquals(100, var, "Different username and password was not executed"); + + } + +} diff --git a/extensions/flyway/deployment/src/test/resources/config-for-different-username.properties b/extensions/flyway/deployment/src/test/resources/config-for-different-username.properties new file mode 100755 index 0000000000000..36631b0b81668 --- /dev/null +++ b/extensions/flyway/deployment/src/test/resources/config-for-different-username.properties @@ -0,0 +1,4 @@ +# Flyway config properties +quarkus.flyway.username=sa +quarkus.flyway.password=sa + diff --git a/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/DataSourceDecorator.java b/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/DataSourceDecorator.java new file mode 100644 index 0000000000000..61b610af943c7 --- /dev/null +++ b/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/DataSourceDecorator.java @@ -0,0 +1,72 @@ +package io.quarkus.flyway.runtime; + +import java.io.PrintWriter; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; +import java.util.Optional; +import java.util.logging.Logger; + +import javax.sql.DataSource; + +public class DataSourceDecorator implements DataSource { + + private DataSource dataSource; + private Optional username; + private Optional password; + + public DataSourceDecorator(DataSource datasource, Optional username, Optional password) { + + this.dataSource = datasource; + this.username = username; + this.password = password; + + } + + public PrintWriter getLogWriter() throws SQLException { + // TODO Auto-generated method stub + return dataSource.getLogWriter(); + } + + public int getLoginTimeout() throws SQLException { + // TODO Auto-generated method stub + return dataSource.getLoginTimeout(); + } + + public Logger getParentLogger() throws SQLFeatureNotSupportedException { + // TODO Auto-generated method stub + return dataSource.getParentLogger(); + } + + public void setLogWriter(PrintWriter arg0) throws SQLException { + // TODO Auto-generated method stub + dataSource.setLogWriter(arg0); + } + + public void setLoginTimeout(int arg0) throws SQLException { + // TODO Auto-generated method stub + dataSource.setLoginTimeout(arg0); + } + + public boolean isWrapperFor(Class arg0) throws SQLException { + // TODO Auto-generated method stub + return dataSource.isWrapperFor(arg0); + } + + public T unwrap(Class arg0) throws SQLException { + // TODO Auto-generated method stub + return dataSource.unwrap(arg0); + } + + public Connection getConnection() throws SQLException { + // TODO Auto-generated method stub + return dataSource.getConnection(username.get(), password.get()); + } + + public Connection getConnection(String username, String password) + throws SQLException { + // TODO Auto-generated method stub + return dataSource.getConnection(username, password); + } + +} diff --git a/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayCreator.java b/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayCreator.java old mode 100644 new mode 100755 index 47dc567decbaf..a80e9fd10ffa6 --- a/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayCreator.java +++ b/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayCreator.java @@ -33,7 +33,13 @@ public FlywayCreator withCallbacks(Collection callbacks) { public Flyway createFlyway(DataSource dataSource) { FluentConfiguration configure = Flyway.configure(); - configure.dataSource(dataSource); + + if (flywayRuntimeConfig.username.isPresent() && flywayRuntimeConfig.password.isPresent()) { + configure.dataSource( + new DataSourceDecorator(dataSource, flywayRuntimeConfig.username, flywayRuntimeConfig.password)); + } else { + configure.dataSource(dataSource); + } if (flywayRuntimeConfig.initSql.isPresent()) { configure.initSql(flywayRuntimeConfig.initSql.get()); } diff --git a/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayDataSourceRuntimeConfig.java b/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayDataSourceRuntimeConfig.java old mode 100644 new mode 100755 index 9f9b05c9ada41..753e3d80f2293 --- a/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayDataSourceRuntimeConfig.java +++ b/extensions/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/FlywayDataSourceRuntimeConfig.java @@ -43,6 +43,26 @@ public static FlywayDataSourceRuntimeConfig defaultConfig() { @ConfigItem public Optional defaultSchema = Optional.empty(); + /** + * This is a safety measurement against theoretical situations where the application goes haywire for any reason. + * This includes e.g. accidentally setting quarkus.hibernate-orm.database.generation to drop-and-create. + * If the application runs with a restricted database user, damage in such situations can be mitigated. + * # Name of a custom database user for flyway to use. Must have DDL rights + * quarkus.flyway.username=my_schema_owner + * + */ + + @ConfigItem + public Optional username = Optional.empty(); + + /** + * Set the password for connection; + * quarkus.flyway.password=my_secret_password + */ + + @ConfigItem + public Optional password = Optional.empty(); + /** * Comma-separated case-sensitive list of schemas managed by Flyway. * The first schema in the list will be automatically set as the default one during the migration.