Skip to content

Commit 5c166ad

Browse files
author
bnasslahsen
committed
Add support for custom OAuth 2.0 configuration, of the swagger-ui. Fixes #333
1 parent effe760 commit 5c166ad

File tree

13 files changed

+456
-53
lines changed

13 files changed

+456
-53
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package org.springdoc.core;
2+
3+
import org.apache.commons.lang3.StringUtils;
4+
import org.springframework.util.CollectionUtils;
5+
6+
import java.util.List;
7+
import java.util.Map;
8+
9+
public abstract class SpringDocPropertiesUtils {
10+
11+
private SpringDocPropertiesUtils() { }
12+
13+
public static void put(String name, List<String> value, Map<String, Object> params) {
14+
if (!CollectionUtils.isEmpty(value)) {
15+
params.put(name, value);
16+
}
17+
}
18+
19+
public static void put(final String name, final Integer value, final Map<String, Object> params) {
20+
if (value != null) {
21+
params.put(name, value.toString());
22+
}
23+
}
24+
25+
public static void put(final String name, final Boolean value, final Map<String, Object> params) {
26+
if (value != null) {
27+
params.put(name, value.toString());
28+
}
29+
}
30+
31+
public static void put(final String name, final String value, final Map<String, Object> params) {
32+
if (!StringUtils.isEmpty(value)) {
33+
params.put(name, value);
34+
}
35+
}
36+
37+
}

Diff for: springdoc-openapi-common/src/main/java/org/springdoc/core/SwaggerUiConfigProperties.java

+26-45
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package org.springdoc.core;
22

3-
43
import org.apache.commons.lang3.StringUtils;
54
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
65
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -137,55 +136,30 @@ public static void addUrl(String url) {
137136

138137
public Map<String, Object> getConfigParameters() {
139138
final Map<String, Object> params = new TreeMap<>();
140-
put("layout", layout, params);
141-
put(CONFIG_URL_PROPERTY, configUrl, params);
142-
put("validatorUrl", validatorUrl, params);
143-
put("filter", filter, params);
144-
put("deepLinking", this.deepLinking, params);
145-
put("displayOperationId", displayOperationId, params);
146-
put("defaultModelsExpandDepth", defaultModelsExpandDepth, params);
147-
put("defaultModelExpandDepth", defaultModelExpandDepth, params);
148-
put("defaultModelRendering", defaultModelRendering, params);
149-
put("displayRequestDuration", displayRequestDuration, params);
150-
put("docExpansion", docExpansion, params);
151-
put("maxDisplayedTags", maxDisplayedTags, params);
152-
put("showExtensions", showExtensions, params);
153-
put("showCommonExtensions", showCommonExtensions, params);
154-
put("operationsSorter", operationsSorter, params);
155-
put("tagsSorter", tagsSorter, params);
139+
SpringDocPropertiesUtils.put("layout", layout, params);
140+
SpringDocPropertiesUtils.put(CONFIG_URL_PROPERTY, configUrl, params);
141+
SpringDocPropertiesUtils.put("validatorUrl", validatorUrl, params);
142+
SpringDocPropertiesUtils.put("filter", filter, params);
143+
SpringDocPropertiesUtils.put("deepLinking", this.deepLinking, params);
144+
SpringDocPropertiesUtils.put("displayOperationId", displayOperationId, params);
145+
SpringDocPropertiesUtils.put("defaultModelsExpandDepth", defaultModelsExpandDepth, params);
146+
SpringDocPropertiesUtils.put("defaultModelExpandDepth", defaultModelExpandDepth, params);
147+
SpringDocPropertiesUtils.put("defaultModelRendering", defaultModelRendering, params);
148+
SpringDocPropertiesUtils.put("displayRequestDuration", displayRequestDuration, params);
149+
SpringDocPropertiesUtils.put("docExpansion", docExpansion, params);
150+
SpringDocPropertiesUtils.put("maxDisplayedTags", maxDisplayedTags, params);
151+
SpringDocPropertiesUtils.put("showExtensions", showExtensions, params);
152+
SpringDocPropertiesUtils.put("showCommonExtensions", showCommonExtensions, params);
153+
SpringDocPropertiesUtils.put("operationsSorter", operationsSorter, params);
154+
SpringDocPropertiesUtils.put("tagsSorter", tagsSorter, params);
156155
if (!CollectionUtils.isEmpty(supportedSubmitMethods))
157-
put("supportedSubmitMethods", supportedSubmitMethods.toString(), params);
158-
put("oauth2RedirectUrl", oauth2RedirectUrl, params);
159-
put("url", url, params);
156+
SpringDocPropertiesUtils.put("supportedSubmitMethods", supportedSubmitMethods.toString(), params);
157+
SpringDocPropertiesUtils.put("oauth2RedirectUrl", oauth2RedirectUrl, params);
158+
SpringDocPropertiesUtils.put("url", url, params);
160159
put("urls", swaggerUrls, params);
161160
return params;
162161
}
163162

164-
protected void put(String urls, List<SwaggerUrl> swaggerUrls, Map<String, Object> params) {
165-
swaggerUrls = swaggerUrls.stream().filter(elt -> StringUtils.isNotEmpty(elt.getUrl())).collect(Collectors.toList());
166-
if (!CollectionUtils.isEmpty(swaggerUrls)) {
167-
params.put(urls, swaggerUrls);
168-
}
169-
}
170-
171-
protected void put(final String name, final Integer value, final Map<String, Object> params) {
172-
if (value != null) {
173-
params.put(name, value.toString());
174-
}
175-
}
176-
177-
protected void put(final String name, final Boolean value, final Map<String, Object> params) {
178-
if (value != null) {
179-
params.put(name, value.toString());
180-
}
181-
}
182-
183-
protected void put(final String name, final String value, final Map<String, Object> params) {
184-
if (!StringUtils.isEmpty(value)) {
185-
params.put(name, value);
186-
}
187-
}
188-
189163
public String getValidatorUrl() {
190164
return validatorUrl;
191165
}
@@ -384,4 +358,11 @@ public void setName(String name) {
384358
this.name = name;
385359
}
386360
}
361+
362+
private void put(String urls, List<SwaggerUrl> swaggerUrls, Map<String, Object> params) {
363+
swaggerUrls = swaggerUrls.stream().filter(elt -> StringUtils.isNotEmpty(elt.getUrl())).collect(Collectors.toList());
364+
if (!CollectionUtils.isEmpty(swaggerUrls)) {
365+
params.put(urls, swaggerUrls);
366+
}
367+
}
387368
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package org.springdoc.core;
2+
3+
4+
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
5+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
6+
import org.springframework.boot.context.properties.ConfigurationProperties;
7+
import org.springframework.context.annotation.Configuration;
8+
9+
import java.util.List;
10+
import java.util.Map;
11+
import java.util.TreeMap;
12+
13+
import static org.springdoc.core.Constants.SPRINGDOC_SWAGGER_UI_ENABLED;
14+
15+
/**
16+
* Please refer to the swagger
17+
* <a href="https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/oauth2.md">configuration.md</a>
18+
* to get the idea what each parameter does.
19+
*/
20+
@Configuration
21+
@ConfigurationProperties(prefix = "springdoc.swagger-ui.oauth")
22+
@ConditionalOnProperty(name = SPRINGDOC_SWAGGER_UI_ENABLED, matchIfMissing = true)
23+
@ConditionalOnBean(SpringDocConfiguration.class)
24+
public class SwaggerUiOAuthProperties {
25+
26+
private String clientId;
27+
private String clientSecret;
28+
private String realm;
29+
private String appName;
30+
private String scopeSeparator;
31+
private List<String> additionalQueryStringParams;
32+
private String useBasicAuthenticationWithAccessCodeGrant;
33+
private Boolean usePkceWithAuthorizationCodeGrant;
34+
35+
36+
public Map<String, Object> getConfigParameters() {
37+
final Map<String, Object> params = new TreeMap<>();
38+
SpringDocPropertiesUtils.put("clientId", clientId, params);
39+
SpringDocPropertiesUtils.put("clientSecret", clientSecret, params);
40+
SpringDocPropertiesUtils.put("realm", realm, params);
41+
SpringDocPropertiesUtils.put("scopeSeparator", scopeSeparator, params);
42+
SpringDocPropertiesUtils.put("appName", appName, params);
43+
SpringDocPropertiesUtils.put("useBasicAuthenticationWithAccessCodeGrant", useBasicAuthenticationWithAccessCodeGrant, params);
44+
SpringDocPropertiesUtils.put("usePkceWithAuthorizationCodeGrant", usePkceWithAuthorizationCodeGrant, params);
45+
SpringDocPropertiesUtils.put("additionalQueryStringParams", additionalQueryStringParams, params);
46+
return params;
47+
}
48+
49+
public String getClientId() {
50+
return clientId;
51+
}
52+
53+
public void setClientId(String clientId) {
54+
this.clientId = clientId;
55+
}
56+
57+
public String getClientSecret() {
58+
return clientSecret;
59+
}
60+
61+
public void setClientSecret(String clientSecret) {
62+
this.clientSecret = clientSecret;
63+
}
64+
65+
public String getRealm() {
66+
return realm;
67+
}
68+
69+
public void setRealm(String realm) {
70+
this.realm = realm;
71+
}
72+
73+
public String getAppName() {
74+
return appName;
75+
}
76+
77+
public void setAppName(String appName) {
78+
this.appName = appName;
79+
}
80+
81+
public String getScopeSeparator() {
82+
return scopeSeparator;
83+
}
84+
85+
public void setScopeSeparator(String scopeSeparator) {
86+
this.scopeSeparator = scopeSeparator;
87+
}
88+
89+
public List<String> getAdditionalQueryStringParams() {
90+
return additionalQueryStringParams;
91+
}
92+
93+
public void setAdditionalQueryStringParams(List<String> additionalQueryStringParams) {
94+
this.additionalQueryStringParams = additionalQueryStringParams;
95+
}
96+
97+
public String getUseBasicAuthenticationWithAccessCodeGrant() {
98+
return useBasicAuthenticationWithAccessCodeGrant;
99+
}
100+
101+
public void setUseBasicAuthenticationWithAccessCodeGrant(String useBasicAuthenticationWithAccessCodeGrant) {
102+
this.useBasicAuthenticationWithAccessCodeGrant = useBasicAuthenticationWithAccessCodeGrant;
103+
}
104+
105+
public Boolean getUsePkceWithAuthorizationCodeGrant() {
106+
return usePkceWithAuthorizationCodeGrant;
107+
}
108+
109+
public void setUsePkceWithAuthorizationCodeGrant(Boolean usePkceWithAuthorizationCodeGrant) {
110+
this.usePkceWithAuthorizationCodeGrant = usePkceWithAuthorizationCodeGrant;
111+
}
112+
}

Diff for: springdoc-openapi-ui/src/main/java/org/springdoc/ui/SwaggerConfig.java

+17-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package org.springdoc.ui;
22

3+
import com.fasterxml.jackson.databind.ObjectMapper;
34
import org.springdoc.core.SpringDocConfiguration;
5+
import org.springdoc.core.SwaggerUiOAuthProperties;
6+
import org.springframework.beans.factory.annotation.Autowired;
47
import org.springframework.beans.factory.annotation.Value;
58
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
69
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -25,23 +28,31 @@ public class SwaggerConfig extends WebMvcConfigurerAdapter { // NOSONAR
2528
@Value(WEB_JARS_PREFIX_URL)
2629
private String webJarsPrefixUrl;
2730

31+
@Autowired
32+
private SwaggerIndexTransformer swaggerIndexTransformer;
33+
2834
@Override
2935
public void addResourceHandlers(ResourceHandlerRegistry registry) {
30-
String uiRootPath = "";
36+
StringBuilder uiRootPath = new StringBuilder();
3137
if (swaggerPath.contains("/")) {
32-
uiRootPath = swaggerPath.substring(0, swaggerPath.lastIndexOf('/'));
38+
uiRootPath.append(swaggerPath.substring(0, swaggerPath.lastIndexOf('/')));
3339
}
34-
uiRootPath += "/**";
35-
40+
uiRootPath.append("/**");
3641
String webJarsLocation = webJarsPrefixUrl + DEFAULT_PATH_SEPARATOR;
37-
3842
registry.addResourceHandler(uiRootPath + "/swagger-ui/**").addResourceLocations(webJarsLocation)
39-
.resourceChain(false);
43+
.resourceChain(false)
44+
.addTransformer(swaggerIndexTransformer);
4045
}
4146

4247
@Bean
4348
@ConditionalOnProperty(name = SPRINGDOC_SWAGGER_UI_ENABLED, matchIfMissing = true)
4449
public SwaggerWelcome swaggerWelcome() {
4550
return new SwaggerWelcome();
4651
}
52+
53+
@Bean
54+
@ConditionalOnProperty(name = SPRINGDOC_SWAGGER_UI_ENABLED, matchIfMissing = true)
55+
public SwaggerIndexTransformer indexPageTransformer(SwaggerUiOAuthProperties swaggerUiOAuthProperties, ObjectMapper objectMapper) {
56+
return new SwaggerIndexTransformer(swaggerUiOAuthProperties, objectMapper);
57+
}
4758
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package org.springdoc.ui;
2+
3+
import com.fasterxml.jackson.core.JsonProcessingException;
4+
import com.fasterxml.jackson.databind.ObjectMapper;
5+
import org.springdoc.core.SwaggerUiOAuthProperties;
6+
import org.springframework.core.io.Resource;
7+
import org.springframework.util.AntPathMatcher;
8+
import org.springframework.util.CollectionUtils;
9+
import org.springframework.web.servlet.resource.ResourceTransformer;
10+
import org.springframework.web.servlet.resource.ResourceTransformerChain;
11+
import org.springframework.web.servlet.resource.TransformedResource;
12+
13+
import javax.servlet.http.HttpServletRequest;
14+
import java.io.ByteArrayOutputStream;
15+
import java.io.IOException;
16+
import java.io.InputStream;
17+
import java.nio.charset.StandardCharsets;
18+
19+
public class SwaggerIndexTransformer implements ResourceTransformer {
20+
21+
private SwaggerUiOAuthProperties swaggerUiOAuthProperties;
22+
private ObjectMapper objectMapper;
23+
24+
public SwaggerIndexTransformer(SwaggerUiOAuthProperties swaggerUiOAuthProperties, ObjectMapper objectMapper) {
25+
this.swaggerUiOAuthProperties = swaggerUiOAuthProperties;
26+
this.objectMapper = objectMapper;
27+
}
28+
29+
@Override
30+
public Resource transform(HttpServletRequest request, Resource resource,
31+
ResourceTransformerChain transformerChain) throws IOException {
32+
final AntPathMatcher antPathMatcher = new AntPathMatcher();
33+
boolean isIndexFound = antPathMatcher.match("**/swagger-ui/**/index.html", resource.getURL().toString());
34+
if (isIndexFound && !CollectionUtils.isEmpty(swaggerUiOAuthProperties.getConfigParameters())) {
35+
String html = readFullyAsString(resource.getInputStream());
36+
html = addInitOauth(html);
37+
return new TransformedResource(resource, html.getBytes());
38+
} else {
39+
return resource;
40+
}
41+
}
42+
43+
private String addInitOauth(String html) throws JsonProcessingException {
44+
StringBuilder stringBuilder = new StringBuilder("window.ui = ui\n");
45+
stringBuilder.append("ui.initOAuth(\n");
46+
String json = objectMapper.writeValueAsString(swaggerUiOAuthProperties.getConfigParameters());
47+
stringBuilder.append(json);
48+
stringBuilder.append(")");
49+
String result = html.replace("window.ui = ui", stringBuilder.toString());
50+
return result;
51+
}
52+
53+
private String readFullyAsString(InputStream inputStream)
54+
throws IOException {
55+
ByteArrayOutputStream baos = new ByteArrayOutputStream();
56+
byte[] buffer = new byte[1024];
57+
int length = 0;
58+
while ((length = inputStream.read(buffer)) != -1) {
59+
baos.write(buffer, 0, length);
60+
}
61+
return baos.toString(StandardCharsets.UTF_8.name());
62+
}
63+
}
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
22
org.springdoc.ui.SwaggerConfig,\
3-
org.springdoc.core.SwaggerUiConfigProperties
3+
org.springdoc.core.SwaggerUiConfigProperties,\
4+
org.springdoc.core.SwaggerUiOAuthProperties

0 commit comments

Comments
 (0)