diff --git a/docs/generators/java-microprofile.md b/docs/generators/java-microprofile.md index 2dfaf5e0bcf4..6ee156c99dab 100644 --- a/docs/generators/java-microprofile.md +++ b/docs/generators/java-microprofile.md @@ -56,6 +56,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl |generateBuilders|Whether to generate builders for models| |false| |generateClientAsBean|For resttemplate, restclient and webclient, configure whether to create `ApiClient.java` and Apis clients as bean (with `@Component` annotation).| |false| |generateConstructorWithAllArgs|whether to generate a constructor for all arguments| |false| +|generateInsecureTlsHook|Generate the ApiClient.disableCertificateValidation hook, which trusts all TLS certificates (default to true). Set to false to omit it, e.g. when static analysis flags the trust-all TrustManager it contains. Available on `jersey2`, `jersey3` libraries.| |true| |gradleProperties|Append additional Gradle properties to the gradle.properties file| |null| |groupId|groupId in generated pom.xml| |org.openapitools| |hideGenerationTimestamp|Hides the generation timestamp when files are generated.| |false| diff --git a/docs/generators/java.md b/docs/generators/java.md index 6740780102b9..3cee431791da 100644 --- a/docs/generators/java.md +++ b/docs/generators/java.md @@ -56,6 +56,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl |generateBuilders|Whether to generate builders for models| |false| |generateClientAsBean|For resttemplate, restclient and webclient, configure whether to create `ApiClient.java` and Apis clients as bean (with `@Component` annotation).| |false| |generateConstructorWithAllArgs|whether to generate a constructor for all arguments| |false| +|generateInsecureTlsHook|Generate the ApiClient.disableCertificateValidation hook, which trusts all TLS certificates (default to true). Set to false to omit it, e.g. when static analysis flags the trust-all TrustManager it contains. Available on `jersey2`, `jersey3` libraries.| |true| |gradleProperties|Append additional Gradle properties to the gradle.properties file| |null| |groupId|groupId in generated pom.xml| |org.openapitools| |hideGenerationTimestamp|Hides the generation timestamp when files are generated.| |false| diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java index 6f6cb0af8471..bf0978b1e6de 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java @@ -90,6 +90,7 @@ public class JavaClientCodegen extends AbstractJavaCodegen public static final String DYNAMIC_OPERATIONS = "dynamicOperations"; public static final String SUPPORT_STREAMING = "supportStreaming"; public static final String SUPPORT_URL_QUERY = "supportUrlQuery"; + public static final String GENERATE_INSECURE_TLS_HOOK = "generateInsecureTlsHook"; public static final String GRADLE_PROPERTIES = "gradleProperties"; public static final String ERROR_OBJECT_TYPE = "errorObjectType"; @@ -283,6 +284,7 @@ public JavaClientCodegen() { cliOptions.add(CliOption.newBoolean(WEBCLIENT_BLOCKING_OPERATIONS, "Making all WebClient operations blocking(sync). Note that if on operation 'x-webclient-blocking: false' then such operation won't be sync", this.webclientBlockingOperations)); cliOptions.add(CliOption.newBoolean(GENERATE_CLIENT_AS_BEAN, "For resttemplate, restclient and webclient, configure whether to create `ApiClient.java` and Apis clients as bean (with `@Component` annotation).", this.generateClientAsBean)); cliOptions.add(CliOption.newBoolean(SUPPORT_URL_QUERY, "Generate toUrlQueryString in POJO (default to true). Available on `native`, `apache-httpclient` libraries.")); + cliOptions.add(CliOption.newBoolean(GENERATE_INSECURE_TLS_HOOK, "Generate the ApiClient.disableCertificateValidation hook, which trusts all TLS certificates (default to true). Set to false to omit it, e.g. when static analysis flags the trust-all TrustManager it contains. Available on `jersey2`, `jersey3` libraries.", true)); cliOptions.add(CliOption.newBoolean(USE_ENUM_CASE_INSENSITIVE, "Use `equalsIgnoreCase` when String for enum comparison", useEnumCaseInsensitive)); cliOptions.add(CliOption.newBoolean(FAIL_ON_UNKNOWN_PROPERTIES, "Fail Jackson de-serialization on unknown properties", this.failOnUnknownProperties)); cliOptions.add(CliOption.newBoolean(USE_JACKSON_3, "Use Jackson 3 instead of Jackson 2. Supported for 'native', 'apache-httpclient', and 'jersey3' libraries (requires Java 17+) and for Spring 'resttemplate', 'webclient', and 'restclient' libraries (require useSpringBoot4=true).", this.useJackson3)); @@ -527,6 +529,13 @@ public void processOpts() { additionalProperties.put(SUPPORT_URL_QUERY, Boolean.parseBoolean(additionalProperties.get(SUPPORT_URL_QUERY).toString())); } + if (!additionalProperties.containsKey(GENERATE_INSECURE_TLS_HOOK)) { + additionalProperties.put(GENERATE_INSECURE_TLS_HOOK, true); + } else { + additionalProperties.put(GENERATE_INSECURE_TLS_HOOK, + Boolean.parseBoolean(additionalProperties.get(GENERATE_INSECURE_TLS_HOOK).toString())); + } + convertPropertyToBooleanAndWriteBack(GENERATE_CLIENT_AS_BEAN, this::setGenerateClientAsBean); convertPropertyToBooleanAndWriteBack(USE_ENUM_CASE_INSENSITIVE, this::setUseEnumCaseInsensitive); convertPropertyToTypeAndWriteBack(CodegenConstants.MAX_ATTEMPTS_FOR_RETRY, Integer::parseInt, this::setMaxAttemptsForRetry); diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/jersey2/ApiClient.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/jersey2/ApiClient.mustache index 2de6367d903d..bf1136d4e7ef 100644 --- a/modules/openapi-generator/src/main/resources/Java/libraries/jersey2/ApiClient.mustache +++ b/modules/openapi-generator/src/main/resources/Java/libraries/jersey2/ApiClient.mustache @@ -29,6 +29,7 @@ import java.io.IOException; import java.io.InputStream; import java.net.URI; +{{#generateInsecureTlsHook}} import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; @@ -36,6 +37,7 @@ import java.security.cert.X509Certificate; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; +{{/generateInsecureTlsHook}} import java.nio.file.Files; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; @@ -1437,14 +1439,17 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} { * server endpoints from web targets created by the client instance that is using this SSL context. * 4. Set the client-side trust store. * +{{#generateInsecureTlsHook}} * To completely disable certificate validation (at your own risk), you can * override this method and invoke disableCertificateValidation(clientBuilder). * +{{/generateInsecureTlsHook}} * @param clientBuilder a {@link {{javaxPackage}}.ws.rs.client.ClientBuilder} object. */ protected void customizeClientBuilder(ClientBuilder clientBuilder) { // No-op extension point } +{{#generateInsecureTlsHook}} /** * Disable X.509 certificate validation in TLS connections. @@ -1475,6 +1480,7 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} { sslContext.init(null, trustAllCerts, new SecureRandom()); clientBuilder.sslContext(sslContext); } +{{/generateInsecureTlsHook}} /** *

Build the response headers.

diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/jersey3/ApiClient.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/jersey3/ApiClient.mustache index c5d1cbcb64b5..dcab60b8f93a 100644 --- a/modules/openapi-generator/src/main/resources/Java/libraries/jersey3/ApiClient.mustache +++ b/modules/openapi-generator/src/main/resources/Java/libraries/jersey3/ApiClient.mustache @@ -34,6 +34,7 @@ import java.io.IOException; import java.io.InputStream; import java.net.URI; +{{#generateInsecureTlsHook}} import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; @@ -41,6 +42,7 @@ import java.security.cert.X509Certificate; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; +{{/generateInsecureTlsHook}} import java.nio.file.Files; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; @@ -1477,14 +1479,17 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} { * server endpoints from web targets created by the client instance that is using this SSL context. * 4. Set the client-side trust store. * +{{#generateInsecureTlsHook}} * To completely disable certificate validation (at your own risk), you can * override this method and invoke disableCertificateValidation(clientBuilder). * +{{/generateInsecureTlsHook}} * @param clientBuilder a {@link {{javaxPackage}}.ws.rs.client.ClientBuilder} object. */ protected void customizeClientBuilder(ClientBuilder clientBuilder) { // No-op extension point } +{{#generateInsecureTlsHook}} /** * Disable X.509 certificate validation in TLS connections. @@ -1515,6 +1520,7 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} { sslContext.init(null, trustAllCerts, new SecureRandom()); clientBuilder.sslContext(sslContext); } +{{/generateInsecureTlsHook}} /** *

Build the response headers.

diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java index b3940c9078ba..d21e044d2db9 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java @@ -5003,4 +5003,47 @@ private static JavaClientCodegen newRetrofit2Codegen(Map propert codegen.additionalProperties().putAll(properties); return codegen; } + + @DataProvider(name = "jerseyLibraries") + public static Object[][] jerseyLibraries() { + return new Object[][]{{JavaClientCodegen.JERSEY2}, {JavaClientCodegen.JERSEY3}}; + } + + @Test(dataProvider = "jerseyLibraries") + public void testInsecureTlsHookGeneratedByDefault(String library) { + Path output = generateJerseyClient(library, null); + + JavaFileAssert.assertThat(output.resolve("src/main/java/xyz/abcdef/invoker/ApiClient.java").toFile()) + .assertMethod("disableCertificateValidation"); + } + + @Test(dataProvider = "jerseyLibraries") + public void testInsecureTlsHookOmittedWhenDisabled(String library) { + Path output = generateJerseyClient(library, false); + + assertThat(output.resolve("src/main/java/xyz/abcdef/invoker/ApiClient.java")).content() + .doesNotContain("disableCertificateValidation") + .doesNotContain("X509TrustManager") + .doesNotContain("import javax.net.ssl.SSLContext;") + .doesNotContain("import java.security.SecureRandom;") + .doesNotContain("import java.security.KeyManagementException;") + .doesNotContain("import java.security.NoSuchAlgorithmException;") + .doesNotContain("import java.security.cert.X509Certificate;"); + } + + private static Path generateJerseyClient(String library, Boolean generateInsecureTlsHook) { + Path output = newTempFolder(); + CodegenConfigurator configurator = new CodegenConfigurator() + .setGeneratorName(JAVA_GENERATOR) + .setLibrary(library) + .addAdditionalProperty(CodegenConstants.INVOKER_PACKAGE, "xyz.abcdef.invoker") + .setInputSpec("src/test/resources/3_0/petstore.yaml") + .setOutputDir(output.toString().replace("\\", "/")); + if (generateInsecureTlsHook != null) { + configurator.addAdditionalProperty(GENERATE_INSECURE_TLS_HOOK, generateInsecureTlsHook); + } + + new DefaultGenerator().opts(configurator.toClientOptInput()).generate(); + return output; + } }