You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by re...@apache.org on 2022/02/14 17:08:29 UTC
[cxf] branch master updated: CXF-8636: Swagger2Feature: Can't set url in UI through SwaggerUiConfig (#906)
This is an automated email from the ASF dual-hosted git repository.
reta pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cxf.git
The following commit(s) were added to refs/heads/master by this push:
new 90fd6d4 CXF-8636: Swagger2Feature: Can't set url in UI through SwaggerUiConfig (#906)
90fd6d4 is described below
commit 90fd6d465c7b9bbe108aa6c744744ce19fecd93d
Author: Andriy Redko <dr...@gmail.com>
AuthorDate: Mon Feb 14 12:08:21 2022 -0500
CXF-8636: Swagger2Feature: Can't set url in UI through SwaggerUiConfig (#906)
---
distribution/src/main/release/samples/pom.xml | 2 +-
parent/pom.xml | 2 +-
.../cxf/jaxrs/swagger/ui/SwaggerUiConfig.java | 15 +++++
.../cxf/jaxrs/swagger/ui/SwaggerUiService.java | 73 +++++++++++++++++-----
... => SwaggerUiConfigurationQueryConfigTest.java} | 23 ++-----
.../description/SwaggerUiConfigurationTest.java | 18 ++++++
6 files changed, 98 insertions(+), 35 deletions(-)
diff --git a/distribution/src/main/release/samples/pom.xml b/distribution/src/main/release/samples/pom.xml
index 6b992f8..fe0d6e6 100644
--- a/distribution/src/main/release/samples/pom.xml
+++ b/distribution/src/main/release/samples/pom.xml
@@ -35,7 +35,7 @@
<cxf.jetty9.version>9.4.45.v20220203</cxf.jetty9.version>
<cxf.netty.version>4.1.74.Final</cxf.netty.version>
<cxf.httpcomponents.client.version>4.5.13</cxf.httpcomponents.client.version>
- <cxf.swagger.ui.version>4.1.2</cxf.swagger.ui.version>
+ <cxf.swagger.ui.version>4.5.0</cxf.swagger.ui.version>
<cxf.tika.version>2.2.1</cxf.tika.version>
<cxf.tomcat.version>9.0.58</cxf.tomcat.version>
<graalvm.version>21.1.0</graalvm.version>
diff --git a/parent/pom.xml b/parent/pom.xml
index 861c138..c800c94 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -205,7 +205,7 @@
<cxf.spring.security.version>5.6.1</cxf.spring.security.version>
<cxf.spring.version>5.3.15</cxf.spring.version>
<cxf.stax-ex.version>1.8.3</cxf.stax-ex.version>
- <cxf.swagger.ui.version>4.1.2</cxf.swagger.ui.version>
+ <cxf.swagger.ui.version>4.5.0</cxf.swagger.ui.version>
<cxf.swagger.v3.version>2.1.13</cxf.swagger.v3.version>
<cxf.swagger2.version>1.6.5</cxf.swagger2.version>
<cxf.swagger2.guava.version>31.0.1-jre</cxf.swagger2.guava.version>
diff --git a/rt/rs/description-swagger-ui/src/main/java/org/apache/cxf/jaxrs/swagger/ui/SwaggerUiConfig.java b/rt/rs/description-swagger-ui/src/main/java/org/apache/cxf/jaxrs/swagger/ui/SwaggerUiConfig.java
index 940bdbd..446a39b 100644
--- a/rt/rs/description-swagger-ui/src/main/java/org/apache/cxf/jaxrs/swagger/ui/SwaggerUiConfig.java
+++ b/rt/rs/description-swagger-ui/src/main/java/org/apache/cxf/jaxrs/swagger/ui/SwaggerUiConfig.java
@@ -63,6 +63,8 @@ public class SwaggerUiConfig {
private String validatorUrl;
// Controls whether the "Try it out" section should be enabled by default.
private Boolean tryItOutEnabled;
+ // Enables overriding configuration parameters via URL search params.
+ private Boolean queryConfigEnabled;
public String getConfigUrl() {
return configUrl;
@@ -242,6 +244,11 @@ public class SwaggerUiConfig {
return this;
}
+ public SwaggerUiConfig queryConfigEnabled(boolean enabled) {
+ setQueryConfigEnabled(enabled);
+ return this;
+ }
+
public SwaggerUiConfig filter(final String f) {
setFilter(f);
return this;
@@ -252,6 +259,14 @@ public class SwaggerUiConfig {
return this;
}
+ public Boolean isQueryConfigEnabled() {
+ return queryConfigEnabled;
+ }
+
+ public void setQueryConfigEnabled(Boolean queryConfigEnabled) {
+ this.queryConfigEnabled = queryConfigEnabled;
+ }
+
public Map<String, String> getConfigParameters() {
final Map<String, String> params = new TreeMap<>();
diff --git a/rt/rs/description-swagger-ui/src/main/java/org/apache/cxf/jaxrs/swagger/ui/SwaggerUiService.java b/rt/rs/description-swagger-ui/src/main/java/org/apache/cxf/jaxrs/swagger/ui/SwaggerUiService.java
index 615a665..bfccf12 100644
--- a/rt/rs/description-swagger-ui/src/main/java/org/apache/cxf/jaxrs/swagger/ui/SwaggerUiService.java
+++ b/rt/rs/description-swagger-ui/src/main/java/org/apache/cxf/jaxrs/swagger/ui/SwaggerUiService.java
@@ -20,9 +20,12 @@
package org.apache.cxf.jaxrs.swagger.ui;
import java.io.IOException;
+import java.io.InputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import javax.ws.rs.GET;
import javax.ws.rs.NotFoundException;
@@ -34,11 +37,15 @@ import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
+import org.apache.cxf.common.util.StringUtils;
+import org.apache.cxf.helpers.IOUtils;
+
@Path("api-docs")
public class SwaggerUiService {
private static final String FAVICON = "favicon";
private static final Map<String, String> DEFAULT_MEDIA_TYPES;
+ private static final Pattern URL_PATTERN = Pattern.compile("url[:]\\s*[\"]([^\"]+)[\"][,]");
static {
DEFAULT_MEDIA_TYPES = new HashMap<>();
@@ -101,22 +108,42 @@ public class SwaggerUiService {
// http://localhost:8080/services/helloservice/api-docs?url=/services/helloservice/openapi.json
//
// in case the "url" configuration parameter is provided for Swagger UI.
- if (config != null && uriInfo.getQueryParameters().isEmpty() && path.endsWith("/index.html")) {
- final Map<String, String> params = config.getConfigParameters();
-
- if (params != null && !params.isEmpty()) {
- final UriBuilder builder = params
- .entrySet()
- .stream()
- .reduce(
- uriInfo.getRequestUriBuilder(),
- (b, e) -> b.queryParam(e.getKey(), e.getValue()),
- (left, right) -> left
- );
- return Response.temporaryRedirect(builder.build()).build();
+ if (config != null && path.endsWith("/index.html")) {
+ if (uriInfo.getQueryParameters().isEmpty()) {
+ final Map<String, String> params = config.getConfigParameters();
+
+ if (params != null && !params.isEmpty()) {
+ final UriBuilder builder = params
+ .entrySet()
+ .stream()
+ .reduce(
+ uriInfo.getRequestUriBuilder(),
+ (b, e) -> b.queryParam(e.getKey(), e.getValue()),
+ (left, right) -> left
+ );
+ return Response.temporaryRedirect(builder.build()).build();
+ }
+ }
+
+ // Since Swagger UI 4.1.3, passing the default URL as query parameter,
+ // e.g. `?url=swagger.json` is disabled by default due to security concerns.
+ if (config.isQueryConfigEnabled() == null || !config.isQueryConfigEnabled()) {
+ final String url = config.getUrl();
+ if (!StringUtils.isEmpty(url)) {
+ try (InputStream in = resourceURL.openStream()) {
+ final String index = replaceUrl(IOUtils.readStringFromStream(in), url);
+ final ResponseBuilder rb = Response.ok(index);
+
+ if (mediaType != null) {
+ rb.type(mediaType);
+ }
+
+ return rb.build();
+ }
+ }
}
}
-
+
ResponseBuilder rb = Response.ok(resourceURL.openStream());
if (mediaType != null) {
rb.type(mediaType);
@@ -126,5 +153,23 @@ public class SwaggerUiService {
throw new NotFoundException(ex);
}
}
+
+ /**
+ * Replaces the URL inside Swagger UI index.html file. The implementation does not attempt to
+ * read the file and parse it as valid HTML but uses straightforward approach by looking for
+ * the URL pattern of the SwaggerUIBundle initialization and replacing it.
+ * @param index index.html file content
+ * @param replacement replacement URL
+ * @return index.html file content with replaced URL
+ */
+ protected String replaceUrl(final String index, final String replacement) {
+ final Matcher matcher = URL_PATTERN.matcher(index);
+
+ if (matcher.find()) {
+ return index.substring(0, matcher.start(1)) + replacement + index.substring(matcher.end(1));
+ }
+
+ return index;
+ }
}
diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/SwaggerUiConfigurationTest.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/SwaggerUiConfigurationQueryConfigTest.java
similarity index 83%
copy from systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/SwaggerUiConfigurationTest.java
copy to systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/SwaggerUiConfigurationQueryConfigTest.java
index 5904c35..3e64231 100644
--- a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/SwaggerUiConfigurationTest.java
+++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/SwaggerUiConfigurationQueryConfigTest.java
@@ -43,8 +43,8 @@ import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertTrue;
-public class SwaggerUiConfigurationTest extends AbstractClientServerTestBase {
- private static final String PORT = allocatePort(SwaggerUiConfigurationTest.class);
+public class SwaggerUiConfigurationQueryConfigTest extends AbstractClientServerTestBase {
+ private static final String PORT = allocatePort(SwaggerUiConfigurationQueryConfigTest.class);
public static class Server extends AbstractServerTestServerBase {
@@ -57,7 +57,7 @@ public class SwaggerUiConfigurationTest extends AbstractClientServerTestBase {
sf.setProvider(new JacksonJsonProvider());
final Swagger2Feature feature = new Swagger2Feature();
feature.setRunAsFilter(false);
- feature.setSwaggerUiConfig(new SwaggerUiConfig().url("/swagger.json"));
+ feature.setSwaggerUiConfig(new SwaggerUiConfig().url("/swagger.json").queryConfigEnabled(true));
sf.setFeatures(Arrays.asList(feature));
sf.setAddress("http://localhost:" + PORT + "/");
return sf.create();
@@ -76,22 +76,6 @@ public class SwaggerUiConfigurationTest extends AbstractClientServerTestBase {
}
@Test
- public void testUiRootResourceRedirect() {
- // Test that Swagger UI resources do not interfere with
- // application-specific ones and are accessible.
- final String url = "http://localhost:" + getPort() + "/api-docs";
-
- WebClient uiClient = WebClient
- .create(url)
- .accept("*/*");
-
- try (Response response = uiClient.get()) {
- assertThat(response.getStatus(), equalTo(Response.Status.TEMPORARY_REDIRECT.getStatusCode()));
- assertThat(response.getHeaderString("Location"), equalTo(url + "?url=/swagger.json"));
- }
- }
-
- @Test
public void testUiRootResource() {
// Test that Swagger UI resources do not interfere with
// application-specific ones and are accessible.
@@ -103,6 +87,7 @@ public class SwaggerUiConfigurationTest extends AbstractClientServerTestBase {
try (Response response = uiClient.get()) {
String html = response.readEntity(String.class);
assertThat(html, containsString("<!-- HTML"));
+ assertThat(html, containsString("url: \"https://petstore.swagger.io/v2/swagger.json\","));
assertThat(response.getMediaType(), equalTo(MediaType.TEXT_HTML_TYPE));
}
}
diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/SwaggerUiConfigurationTest.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/SwaggerUiConfigurationTest.java
index 5904c35..fe821e5 100644
--- a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/SwaggerUiConfigurationTest.java
+++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/description/SwaggerUiConfigurationTest.java
@@ -103,6 +103,24 @@ public class SwaggerUiConfigurationTest extends AbstractClientServerTestBase {
try (Response response = uiClient.get()) {
String html = response.readEntity(String.class);
assertThat(html, containsString("<!-- HTML"));
+ assertThat(html, containsString("url: \"/swagger.json\","));
+ assertThat(response.getMediaType(), equalTo(MediaType.TEXT_HTML_TYPE));
+ }
+ }
+
+ @Test
+ public void testUiRootResourcePicksUrlFromConfigurationOnly() {
+ // Test that Swagger UI URL is picked from configuration only and
+ // never from the query string (when query config is disabled).
+ WebClient uiClient = WebClient
+ .create("http://localhost:" + getPort() + "/api-docs")
+ .query("url", "http://malicious.site/swagger.json")
+ .accept("*/*");
+
+ try (Response response = uiClient.get()) {
+ String html = response.readEntity(String.class);
+ assertThat(html, containsString("<!-- HTML"));
+ assertThat(html, containsString("url: \"/swagger.json\","));
assertThat(response.getMediaType(), equalTo(MediaType.TEXT_HTML_TYPE));
}
}