55import java .util .Map ;
66import java .util .Objects ;
77import java .util .Optional ;
8+ import java .util .function .BooleanSupplier ;
89import java .util .function .Consumer ;
910import java .util .function .Supplier ;
1011
2627import org .jboss .logging .Logger ;
2728
2829import static org .keycloak .config .DatabaseOptions .DB ;
30+ import static org .keycloak .config .DatabaseOptions .DB_MARIADB_SSL_MODE ;
31+ import static org .keycloak .config .DatabaseOptions .DB_MSSQL_ENCRYPT ;
32+ import static org .keycloak .config .DatabaseOptions .DB_MSSQL_TRUST_CERTIFICATE ;
33+ import static org .keycloak .config .DatabaseOptions .DB_MYSQL_SSL_MODE ;
34+ import static org .keycloak .config .DatabaseOptions .DB_ORACLE_DN_MATCH ;
35+ import static org .keycloak .config .DatabaseOptions .DB_ORACLE_TLS_TRANSPORT ;
36+ import static org .keycloak .config .DatabaseOptions .DB_POOL_INITIAL_SIZE ;
2937import static org .keycloak .config .DatabaseOptions .DB_POOL_MAX_SIZE ;
38+ import static org .keycloak .config .DatabaseOptions .DB_POSTGRES_SSL_FACTORY ;
39+ import static org .keycloak .config .DatabaseOptions .DB_POSTGRES_SSL_MODE ;
40+ import static org .keycloak .config .DatabaseOptions .DB_TLS_MODE ;
41+ import static org .keycloak .config .DatabaseOptions .DB_TLS_RUSTSTORE_PASSWORD ;
42+ import static org .keycloak .config .DatabaseOptions .DB_TLS_TRUSTSTORE_FILE ;
3043import static org .keycloak .config .DatabaseOptions .Datasources .OPTIONS_DATASOURCES ;
3144import static org .keycloak .config .DatabaseOptions .Datasources .getDatasourceOption ;
3245import static org .keycloak .config .DatabaseOptions .Datasources .getKeyForDatasource ;
@@ -50,26 +63,26 @@ public final class DatabasePropertyMappers implements PropertyMapperGrouping {
5063 public List <PropertyMapper <?>> getPropertyMappers () {
5164 List <PropertyMapper <?>> mappers = List .of (
5265 fromOption (DatabaseOptions .DB_DIALECT )
53- .mapFrom (DatabaseOptions . DB , DatabasePropertyMappers ::transformDialect )
66+ .mapFrom (DB , DatabasePropertyMappers ::transformDialect )
5467 .build (),
5568 fromOption (DatabaseOptions .DB_DRIVER )
56- .mapFrom (DatabaseOptions . DB , DatabasePropertyMappers ::getXaOrNonXaDriver )
69+ .mapFrom (DB , DatabasePropertyMappers ::getXaOrNonXaDriver )
5770 .to ("quarkus.datasource.jdbc.driver" )
5871 .paramLabel ("driver" )
5972 .build (),
60- fromOption (DatabaseOptions . DB )
73+ fromOption (DB )
6174 .to ("quarkus.datasource.db-kind" )
6275 .transformer (DatabasePropertyMappers ::toDatabaseKind )
6376 .paramLabel ("vendor" )
6477 .build (),
6578 fromOption (DatabaseOptions .DB_URL )
6679 .to ("quarkus.datasource.jdbc.url" )
67- .mapFrom (DatabaseOptions . DB , DatabasePropertyMappers ::getDatabaseUrl )
80+ .mapFrom (DB , DatabasePropertyMappers ::getDatabaseUrl )
6881 .paramLabel ("jdbc-url" )
6982 .build (),
7083 fromOption (DatabaseOptions .DB_POSTGRESQL_TARGET_SERVER_TYPE )
7184 .to (PG_TARGET_SERVER_TYPE )
72- .isEnabled (() -> isPostgresqlTargetServerTypeEnabled () )
85+ .isEnabled (DatabasePropertyMappers :: isPostgresqlTargetServerTypeEnabled )
7386 .build (),
7487 fromOption (DatabaseOptions .DB_URL_HOST )
7588 .paramLabel ("hostname" )
@@ -95,16 +108,16 @@ public List<PropertyMapper<?>> getPropertyMappers() {
95108 fromOption (DatabaseOptions .DB_SCHEMA )
96109 .paramLabel ("schema" )
97110 .build (),
98- fromOption (DatabaseOptions . DB_POOL_INITIAL_SIZE )
111+ fromOption (DB_POOL_INITIAL_SIZE )
99112 .to ("quarkus.datasource.jdbc.initial-size" )
100113 .paramLabel ("size" )
101114 .build (),
102115 fromOption (DatabaseOptions .DB_POOL_MIN_SIZE )
103- .mapFrom (DatabaseOptions . DB , DatabasePropertyMappers ::transformMinPoolSize )
116+ .mapFrom (DB , DatabasePropertyMappers ::transformMinPoolSize )
104117 .to ("quarkus.datasource.jdbc.min-size" )
105118 .paramLabel ("size" )
106119 .build (),
107- fromOption (DatabaseOptions . DB_POOL_MAX_SIZE )
120+ fromOption (DB_POOL_MAX_SIZE )
108121 .to ("quarkus.datasource.jdbc.max-size" )
109122 .paramLabel ("size" )
110123 .build (),
@@ -122,20 +135,46 @@ public List<PropertyMapper<?>> getPropertyMappers() {
122135 .to ("quarkus.datasource.\" <datasource>\" .active" )
123136 .build (),
124137 fromOption (DB_URL_PATH )
138+ .build (),
139+ // Database TLS configuration
140+ fromOption (DB_TLS_MODE )
141+ .paramLabel ("mode" )
142+ .build (),
143+ fromOption (DB_TLS_TRUSTSTORE_FILE )
144+ .paramLabel ("path" )
145+ .build (),
146+ fromOption (DB_TLS_RUSTSTORE_PASSWORD )
147+ .paramLabel ("password" )
148+ .isMasked (true )
149+ .build (),
150+ fromOption (DB_ORACLE_TLS_TRANSPORT )
151+ .isEnabled (isVendor (Database .Vendor .ORACLE ))
152+ .mapFrom (DB_TLS_MODE , DatabasePropertyMappers ::transformOracleProtocol )
153+ .build (),
154+ applyTLSJdbcProperty (DB_ORACLE_DN_MATCH , Database .Vendor .ORACLE , "ssl_server_dn_match" ),
155+ applyTLSJdbcProperty (DB_MSSQL_ENCRYPT , Database .Vendor .MSSQL , "encrypt" ),
156+ applyTLSJdbcProperty (DB_MSSQL_TRUST_CERTIFICATE , Database .Vendor .MSSQL , "trustServerCertificate" ),
157+ applyTLSJdbcProperty (DB_MYSQL_SSL_MODE , List .of (Database .Vendor .MYSQL , Database .Vendor .TIDB ), "sslMode" ),
158+ applyTLSJdbcProperty (DB_MARIADB_SSL_MODE , Database .Vendor .MARIADB , "sslMode" ),
159+ applyTLSJdbcProperty (DB_POSTGRES_SSL_MODE , Database .Vendor .POSTGRES , "sslmode" ),
160+ fromOption (DB_POSTGRES_SSL_FACTORY )
161+ .isEnabled (() -> isTLSEnabledAndPropertyNotSet (Database .Vendor .POSTGRES , "sslfactory" ))
162+ .mapFrom (DB_TLS_MODE , DatabasePropertyMappers ::transformPostgresSSLFactory )
163+ .to ("quarkus.datasource.jdbc.additional-jdbc-properties.sslfactory" )
125164 .build ()
126165 );
127166
128167 return appendDatasourceMappers (mappers , Map .of (
129168 // Inherit options from the DB mappers
130- DatabaseOptions . DB , PropertyMapper .Builder ::removeMapFrom ,
131- DatabaseOptions . DB_POOL_INITIAL_SIZE , mapper -> mapper .mapFrom (DatabaseOptions . DB_POOL_INITIAL_SIZE ),
132- DatabaseOptions . DB_POOL_MAX_SIZE , mapper -> mapper .mapFrom (DatabaseOptions . DB_POOL_MAX_SIZE )
169+ DB , PropertyMapper .Builder ::removeMapFrom ,
170+ DB_POOL_INITIAL_SIZE , mapper -> mapper .mapFrom (DB_POOL_INITIAL_SIZE ),
171+ DB_POOL_MAX_SIZE , mapper -> mapper .mapFrom (DB_POOL_MAX_SIZE )
133172 ));
134173 }
135174
136175 @ Override
137176 public void validateConfig (Picocli picocli ) {
138- Configuration .getOptionalIntegerValue (DatabaseOptions . DB_POOL_MAX_SIZE ).ifPresent (poolMaxSize -> {
177+ Configuration .getOptionalIntegerValue (DB_POOL_MAX_SIZE ).ifPresent (poolMaxSize -> {
139178 if (poolMaxSize < JDBC_PING_MIN_POOL_MAX_SIZE && isJdbcPingStack ()) {
140179 throw new PropertyException (
141180 "The JDBC_PING cache stack requires '%s' to be at least %d (current: %d). A higher value is recommended."
@@ -336,4 +375,49 @@ private static String transformDatasourceTo(String to) {
336375 return to ;
337376 }
338377 }
378+
379+ private static String transformOracleProtocol (String key , String value , ConfigSourceInterceptorContext configSourceInterceptorContext ) {
380+ var tlsMode = DatabaseOptions .DatabaseTlsMode .valueOf (value .toUpperCase ());
381+ return tlsMode != DatabaseOptions .DatabaseTlsMode .DISABLED ? "@tcps" : "@" ;
382+ }
383+
384+ private static String transformPostgresSSLFactory (String key , String value , ConfigSourceInterceptorContext configSourceInterceptorContext ) {
385+ // TODO check if db-truststore-file is present
386+ return "org.postgresql.ssl.DefaultJavaSSLFactory" ;
387+ }
388+
389+ private static PropertyMapper <?> applyTLSJdbcProperty (Option <String > option , Database .Vendor vendor , String key ) {
390+ return applyTLSJdbcProperty (option , List .of (vendor ), key );
391+ }
392+
393+ private static PropertyMapper <?> applyTLSJdbcProperty (Option <String > option , List <Database .Vendor > vendors , String key ) {
394+ return fromOption (option )
395+ .isEnabled (() -> vendors .stream ().anyMatch (vendor -> isTLSEnabledAndPropertyNotSet (vendor , key )))
396+ .to ("quarkus.datasource.jdbc.additional-jdbc-properties." + key )
397+ .build ();
398+ }
399+
400+ private static boolean isTLSEnabledAndPropertyNotSet (Database .Vendor expectedVendor , String key ) {
401+ var tlsModeValue = Configuration .getConfigValue (DatabaseOptions .DB_TLS_MODE ).getValue ();
402+ if (tlsModeValue == null ) {
403+ return false ;
404+ }
405+ if (DatabaseOptions .DatabaseTlsMode .valueOf (tlsModeValue .toUpperCase ()) == DatabaseOptions .DatabaseTlsMode .DISABLED ) {
406+ return false ;
407+ }
408+ var configuredVendor = Database .getVendor (Configuration .getConfigValue (DB ).getValue ()).orElseThrow ();
409+ if (configuredVendor != expectedVendor ) {
410+ return false ;
411+ }
412+ String dbUrl = Configuration .getConfigValue (DatabaseOptions .DB_URL ).getValueOrDefault ("" );
413+ // oracle requires tcps protocol for TLS - if not present, TLS is disabled
414+ return (expectedVendor != Database .Vendor .ORACLE || dbUrl .toLowerCase ().contains ("tcps" )) &&
415+ !dbUrl .contains (key );
416+ }
417+
418+ private static BooleanSupplier isVendor (Database .Vendor vendor ) {
419+ return () -> Database .getVendor (Configuration .getConfigValue (DB ).getValue ())
420+ .map (vendor ::equals )
421+ .orElse (Boolean .FALSE );
422+ }
339423}
0 commit comments