From f11ae21612413d39c8b96fb97cb7145e937a017c Mon Sep 17 00:00:00 2001 From: fernando-oc Date: Wed, 25 Sep 2024 17:15:07 +0200 Subject: [PATCH 1/7] This is me --- CONTRIBUTING.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.adoc b/CONTRIBUTING.adoc index fcd6c9a08..c8d953179 100644 --- a/CONTRIBUTING.adoc +++ b/CONTRIBUTING.adoc @@ -7,7 +7,7 @@ If you would like to contribute something, or simply want to hack on the code th This project adheres to the Contributor Covenant link:CODE_OF_CONDUCT.md[code of conduct]. -By participating, you are expected to uphold this code. +By , you are expected to uphold this code. == Using GitHub Issues From 62e75cb830c0289a37164e531c93d39b6cd363be Mon Sep 17 00:00:00 2001 From: fernando-oc Date: Wed, 27 Nov 2024 17:32:32 +0100 Subject: [PATCH 2/7] Swagger Proxy --- .../org/springdoc/core/utils/Constants.java | 8 ++ .../springdoc/webmvc/ui/SwaggerConfig.java | 19 +++++ .../proxy/SwaggerOauthProxyController.java | 84 +++++++++++++++++++ .../proxy/SwaggerOauthProxyProperties.java | 65 ++++++++++++++ 4 files changed, 176 insertions(+) create mode 100644 springdoc-openapi-starter-webmvc-ui/src/main/java/org/springdoc/webmvc/ui/oauth/proxy/SwaggerOauthProxyController.java create mode 100644 springdoc-openapi-starter-webmvc-ui/src/main/java/org/springdoc/webmvc/ui/oauth/proxy/SwaggerOauthProxyProperties.java diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/Constants.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/Constants.java index a3ff8bb9e..810d0540f 100644 --- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/Constants.java +++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/Constants.java @@ -410,6 +410,14 @@ public final class Constants { * The constant SPRINGDOC_NULLABLE_REQUEST_PARAMETER_ENABLED. */ public static final String SPRINGDOC_NULLABLE_REQUEST_PARAMETER_ENABLED = "springdoc.nullable-request-parameter-enabled"; + /** + * The constant SPRINGDOC_SWAGGER_UI_OAUTH_PROXY_PREFIX. + */ + public static final String SPRINGDOC_SWAGGER_UI_OAUTH_PROXY_PREFIX = "springdoc.swagger-ui.oauth-proxy"; + /** + * The constant SPRINGDOC_SWAGGER_UI_OAUTH_PROXY_ENABLED. + */ + public static final String SPRINGDOC_SWAGGER_UI_OAUTH_PROXY_ENABLED = SPRINGDOC_SWAGGER_UI_OAUTH_PROXY_PREFIX + ".enabled"; /** * Instantiates a new Constants. */ diff --git a/springdoc-openapi-starter-webmvc-ui/src/main/java/org/springdoc/webmvc/ui/SwaggerConfig.java b/springdoc-openapi-starter-webmvc-ui/src/main/java/org/springdoc/webmvc/ui/SwaggerConfig.java index 5fcc32f69..c3d5fbb04 100644 --- a/springdoc-openapi-starter-webmvc-ui/src/main/java/org/springdoc/webmvc/ui/SwaggerConfig.java +++ b/springdoc-openapi-starter-webmvc-ui/src/main/java/org/springdoc/webmvc/ui/SwaggerConfig.java @@ -36,6 +36,8 @@ import org.springdoc.core.providers.SpringWebProvider; import org.springdoc.webmvc.core.providers.SpringWebMvcProvider; +import org.springdoc.webmvc.ui.oauth.proxy.SwaggerOauthProxyController; +import org.springdoc.webmvc.ui.oauth.proxy.SwaggerOauthProxyProperties; import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties; import org.springframework.boot.actuate.autoconfigure.web.server.ConditionalOnManagementPort; import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType; @@ -46,11 +48,13 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Lazy; import static org.springdoc.core.utils.Constants.SPRINGDOC_SWAGGER_UI_ENABLED; +import static org.springdoc.core.utils.Constants.SPRINGDOC_SWAGGER_UI_OAUTH_PROXY_ENABLED; import static org.springdoc.core.utils.Constants.SPRINGDOC_USE_MANAGEMENT_PORT; import static org.springdoc.core.utils.Constants.SPRINGDOC_USE_ROOT_PATH; @@ -61,6 +65,7 @@ */ @Lazy(false) @Configuration(proxyBeanMethods = false) +@EnableConfigurationProperties(SwaggerOauthProxyProperties.class) @ConditionalOnProperty(name = SPRINGDOC_SWAGGER_UI_ENABLED, matchIfMissing = true) @ConditionalOnWebApplication(type = Type.SERVLET) @ConditionalOnBean(SpringDocConfiguration.class) @@ -95,6 +100,20 @@ SpringWebProvider springWebProvider() { return new SpringWebMvcProvider(); } + /** + * To delegate Oauth2 authentication + * + * @param swaggerOauthProxyProperties to configure the authorization header + * @return the controller to redirect swagger authentication through application + */ + @Bean + @ConditionalOnMissingBean + @ConditionalOnProperty(name = SPRINGDOC_SWAGGER_UI_OAUTH_PROXY_ENABLED, havingValue = "true") + @Lazy(false) + SwaggerOauthProxyController swaggerOauthProxyController(SwaggerOauthProxyProperties swaggerOauthProxyProperties) { + return new SwaggerOauthProxyController(swaggerOauthProxyProperties); + } + /** * Swagger config resource swagger config resource. * diff --git a/springdoc-openapi-starter-webmvc-ui/src/main/java/org/springdoc/webmvc/ui/oauth/proxy/SwaggerOauthProxyController.java b/springdoc-openapi-starter-webmvc-ui/src/main/java/org/springdoc/webmvc/ui/oauth/proxy/SwaggerOauthProxyController.java new file mode 100644 index 000000000..2855ba5bf --- /dev/null +++ b/springdoc-openapi-starter-webmvc-ui/src/main/java/org/springdoc/webmvc/ui/oauth/proxy/SwaggerOauthProxyController.java @@ -0,0 +1,84 @@ +/* + * + * * + * * * + * * * * + * * * * * Copyright 2019-2024 the original author or authors. + * * * * * + * * * * * Licensed under the Apache License, Version 2.0 (the "License"); + * * * * * you may not use this file except in compliance with the License. + * * * * * You may obtain a copy of the License at + * * * * * + * * * * * https://www.apache.org/licenses/LICENSE-2.0 + * * * * * + * * * * * Unless required by applicable law or agreed to in writing, software + * * * * * distributed under the License is distributed on an "AS IS" BASIS, + * * * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * * * * See the License for the specific language governing permissions and + * * * * * limitations under the License. + * * * * + * * * + * * + * + */ +package org.springdoc.webmvc.ui.oauth.proxy; + +import io.swagger.v3.oas.annotations.Hidden; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestClient; +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +@RestController +public class SwaggerOauthProxyController { + + private final String GRANT_TYPE_KEY = "grant_type"; + private final String CLIENT_CREDENTIALS_KEY = "client_credentials"; + private final String CLIENT_ID_KEY = "client_id"; + private final String CLIENT_SECRET_KEY = "client_secret"; + private final String AUTHENTICATION_SCHEME_BASIC = "Basic"; + + private final RestClient restClient = RestClient.builder().build(); + private final SwaggerOauthProxyProperties swaggerOauthProxyProperties; + + @Autowired + public SwaggerOauthProxyController(SwaggerOauthProxyProperties swaggerOauthProxyProperties) { + this.swaggerOauthProxyProperties = swaggerOauthProxyProperties; + } + + @Hidden + @PostMapping(path = "${springdoc.swagger-ui.oauth-proxy.path}", produces = MediaType.APPLICATION_JSON_VALUE) + public String redirectSwaggerOauth(@RequestHeader(HttpHeaders.AUTHORIZATION) String authorizationHeader) { + + if (authorizationHeader != null && authorizationHeader.startsWith(AUTHENTICATION_SCHEME_BASIC)) { + + String base64Credentials = authorizationHeader.substring(AUTHENTICATION_SCHEME_BASIC.length()).trim(); + String credentials = new String(Base64.getDecoder().decode(base64Credentials), StandardCharsets.UTF_8); + String[] clientDetails = credentials.split(":", 2); + + MultiValueMap body = new LinkedMultiValueMap<>(); + body.add(GRANT_TYPE_KEY, CLIENT_CREDENTIALS_KEY); + body.add(CLIENT_ID_KEY, clientDetails[0]); + body.add(CLIENT_SECRET_KEY, clientDetails[1]); + + ResponseEntity response = restClient.post() + .uri(swaggerOauthProxyProperties.getOauthTokenUri()) + .body(body) + .retrieve() + .toEntity(String.class); + + return response.getBody(); + + } else { + throw new RuntimeException("Authorization header missing or not using Basic Auth"); + } + } +} diff --git a/springdoc-openapi-starter-webmvc-ui/src/main/java/org/springdoc/webmvc/ui/oauth/proxy/SwaggerOauthProxyProperties.java b/springdoc-openapi-starter-webmvc-ui/src/main/java/org/springdoc/webmvc/ui/oauth/proxy/SwaggerOauthProxyProperties.java new file mode 100644 index 000000000..1926dea0b --- /dev/null +++ b/springdoc-openapi-starter-webmvc-ui/src/main/java/org/springdoc/webmvc/ui/oauth/proxy/SwaggerOauthProxyProperties.java @@ -0,0 +1,65 @@ +/* + * + * * + * * * + * * * * + * * * * * Copyright 2019-2024 the original author or authors. + * * * * * + * * * * * Licensed under the Apache License, Version 2.0 (the "License"); + * * * * * you may not use this file except in compliance with the License. + * * * * * You may obtain a copy of the License at + * * * * * + * * * * * https://www.apache.org/licenses/LICENSE-2.0 + * * * * * + * * * * * Unless required by applicable law or agreed to in writing, software + * * * * * distributed under the License is distributed on an "AS IS" BASIS, + * * * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * * * * See the License for the specific language governing permissions and + * * * * * limitations under the License. + * * * * + * * * + * * + * + */ +package org.springdoc.webmvc.ui.oauth.proxy; + +import org.springdoc.core.utils.Constants; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.ConfigurationProperties; +import java.net.URI; +import static org.springdoc.core.utils.Constants.SPRINGDOC_ENABLED; + +@ConfigurationProperties(prefix = Constants.SPRINGDOC_SWAGGER_UI_OAUTH_PROXY_PREFIX) +@ConditionalOnProperty(name = SPRINGDOC_ENABLED, matchIfMissing = true) +public class SwaggerOauthProxyProperties { + + public static final String DEFAULT_PATH = "/forward-creds"; + + private boolean enabled; + private URI path = URI.create(DEFAULT_PATH); + private URI oauthTokenUri; + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public URI getPath() { + return path; + } + + public void setPath(URI path) { + this.path = path; + } + + public URI getOauthTokenUri() { + return oauthTokenUri; + } + + public void setOauthTokenUri(URI oauthTokenUri) { + this.oauthTokenUri = oauthTokenUri; + } +} From 6d2cc64108ce901ff404b65eaa428c2805232307 Mon Sep 17 00:00:00 2001 From: fernando-oc Date: Wed, 27 Nov 2024 17:34:51 +0100 Subject: [PATCH 3/7] fix --- CONTRIBUTING.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.adoc b/CONTRIBUTING.adoc index c8d953179..fcd6c9a08 100644 --- a/CONTRIBUTING.adoc +++ b/CONTRIBUTING.adoc @@ -7,7 +7,7 @@ If you would like to contribute something, or simply want to hack on the code th This project adheres to the Contributor Covenant link:CODE_OF_CONDUCT.md[code of conduct]. -By , you are expected to uphold this code. +By participating, you are expected to uphold this code. == Using GitHub Issues From bf0b276923a20f4905ab03a9a8a1da7a9cb4db24 Mon Sep 17 00:00:00 2001 From: fernando-oc Date: Wed, 27 Nov 2024 17:32:32 +0100 Subject: [PATCH 4/7] Swagger Proxy --- .../src/main/java/org/springdoc/core/utils/Constants.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/Constants.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/Constants.java index 7e03d8c85..3cb8e5761 100644 --- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/Constants.java +++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/Constants.java @@ -414,6 +414,14 @@ public final class Constants { */ public static final String SPRINGDOC_NULLABLE_REQUEST_PARAMETER_ENABLED = "springdoc.nullable-request-parameter-enabled"; + /** + * The constant SPRINGDOC_SWAGGER_UI_OAUTH_PROXY_PREFIX. + */ + public static final String SPRINGDOC_SWAGGER_UI_OAUTH_PROXY_PREFIX = "springdoc.swagger-ui.oauth-proxy"; + /** + * The constant SPRINGDOC_SWAGGER_UI_OAUTH_PROXY_ENABLED. + */ + public static final String SPRINGDOC_SWAGGER_UI_OAUTH_PROXY_ENABLED = SPRINGDOC_SWAGGER_UI_OAUTH_PROXY_PREFIX + ".enabled"; /** * The constant SPRINGDOC_SWAGGER_UI_OAUTH_PROXY_PREFIX. */ From 4a8377aadda4da8b34f44fdf375df29d9892cd71 Mon Sep 17 00:00:00 2001 From: fernando-oc Date: Wed, 27 Nov 2024 17:43:10 +0100 Subject: [PATCH 5/7] Fix duplicates --- .../src/main/java/org/springdoc/core/utils/Constants.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/Constants.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/Constants.java index 3cb8e5761..7e03d8c85 100644 --- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/Constants.java +++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/Constants.java @@ -414,14 +414,6 @@ public final class Constants { */ public static final String SPRINGDOC_NULLABLE_REQUEST_PARAMETER_ENABLED = "springdoc.nullable-request-parameter-enabled"; - /** - * The constant SPRINGDOC_SWAGGER_UI_OAUTH_PROXY_PREFIX. - */ - public static final String SPRINGDOC_SWAGGER_UI_OAUTH_PROXY_PREFIX = "springdoc.swagger-ui.oauth-proxy"; - /** - * The constant SPRINGDOC_SWAGGER_UI_OAUTH_PROXY_ENABLED. - */ - public static final String SPRINGDOC_SWAGGER_UI_OAUTH_PROXY_ENABLED = SPRINGDOC_SWAGGER_UI_OAUTH_PROXY_PREFIX + ".enabled"; /** * The constant SPRINGDOC_SWAGGER_UI_OAUTH_PROXY_PREFIX. */ From cae5ba85a5e554f5ad44e34e6a0d26754315571d Mon Sep 17 00:00:00 2001 From: fernando-oc Date: Thu, 28 Nov 2024 12:50:47 +0100 Subject: [PATCH 6/7] Validate properties --- .../proxy/SwaggerOauthProxyProperties.java | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/springdoc-openapi-starter-webmvc-ui/src/main/java/org/springdoc/webmvc/ui/oauth/proxy/SwaggerOauthProxyProperties.java b/springdoc-openapi-starter-webmvc-ui/src/main/java/org/springdoc/webmvc/ui/oauth/proxy/SwaggerOauthProxyProperties.java index 1926dea0b..25be5c0d8 100644 --- a/springdoc-openapi-starter-webmvc-ui/src/main/java/org/springdoc/webmvc/ui/oauth/proxy/SwaggerOauthProxyProperties.java +++ b/springdoc-openapi-starter-webmvc-ui/src/main/java/org/springdoc/webmvc/ui/oauth/proxy/SwaggerOauthProxyProperties.java @@ -26,17 +26,20 @@ import org.springdoc.core.utils.Constants; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.validation.Errors; +import org.springframework.validation.ValidationUtils; +import org.springframework.validation.Validator; +import org.springframework.validation.annotation.Validated; +import java.io.Serializable; import java.net.URI; import static org.springdoc.core.utils.Constants.SPRINGDOC_ENABLED; +@Validated @ConfigurationProperties(prefix = Constants.SPRINGDOC_SWAGGER_UI_OAUTH_PROXY_PREFIX) -@ConditionalOnProperty(name = SPRINGDOC_ENABLED, matchIfMissing = true) -public class SwaggerOauthProxyProperties { - - public static final String DEFAULT_PATH = "/forward-creds"; +public class SwaggerOauthProxyProperties implements Validator { private boolean enabled; - private URI path = URI.create(DEFAULT_PATH); + private URI path; private URI oauthTokenUri; public boolean isEnabled() { @@ -62,4 +65,21 @@ public URI getOauthTokenUri() { public void setOauthTokenUri(URI oauthTokenUri) { this.oauthTokenUri = oauthTokenUri; } + + @Override + public boolean supports(Class clazz) { + return SwaggerOauthProxyProperties.class.isAssignableFrom(clazz); + } + + @Override + public void validate(Object target, Errors errors) { + if (enabled) { + if (path == null) { + errors.rejectValue("path", "field.required"); + } + if (oauthTokenUri == null) { + errors.rejectValue("oauthTokenUri", "field.required"); + } + } + } } From 3069467493cce32c200d65ae04f6c003d1e76e02 Mon Sep 17 00:00:00 2001 From: fernando-oc Date: Thu, 28 Nov 2024 14:35:39 +0100 Subject: [PATCH 7/7] PR feedback --- .../ui/oauth/proxy/SwaggerOauthProxyController.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/springdoc-openapi-starter-webmvc-ui/src/main/java/org/springdoc/webmvc/ui/oauth/proxy/SwaggerOauthProxyController.java b/springdoc-openapi-starter-webmvc-ui/src/main/java/org/springdoc/webmvc/ui/oauth/proxy/SwaggerOauthProxyController.java index 2855ba5bf..5561f7533 100644 --- a/springdoc-openapi-starter-webmvc-ui/src/main/java/org/springdoc/webmvc/ui/oauth/proxy/SwaggerOauthProxyController.java +++ b/springdoc-openapi-starter-webmvc-ui/src/main/java/org/springdoc/webmvc/ui/oauth/proxy/SwaggerOauthProxyController.java @@ -26,6 +26,7 @@ import io.swagger.v3.oas.annotations.Hidden; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.util.LinkedMultiValueMap; @@ -34,6 +35,7 @@ import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestClient; +import org.springframework.web.server.ResponseStatusException; import java.nio.charset.StandardCharsets; import java.util.Base64; @@ -41,7 +43,7 @@ public class SwaggerOauthProxyController { private final String GRANT_TYPE_KEY = "grant_type"; - private final String CLIENT_CREDENTIALS_KEY = "client_credentials"; + private final String CLIENT_CREDENTIALS_VALUE = "client_credentials"; private final String CLIENT_ID_KEY = "client_id"; private final String CLIENT_SECRET_KEY = "client_secret"; private final String AUTHENTICATION_SCHEME_BASIC = "Basic"; @@ -65,7 +67,7 @@ public String redirectSwaggerOauth(@RequestHeader(HttpHeaders.AUTHORIZATION) Str String[] clientDetails = credentials.split(":", 2); MultiValueMap body = new LinkedMultiValueMap<>(); - body.add(GRANT_TYPE_KEY, CLIENT_CREDENTIALS_KEY); + body.add(GRANT_TYPE_KEY, CLIENT_CREDENTIALS_VALUE); body.add(CLIENT_ID_KEY, clientDetails[0]); body.add(CLIENT_SECRET_KEY, clientDetails[1]); @@ -78,7 +80,7 @@ public String redirectSwaggerOauth(@RequestHeader(HttpHeaders.AUTHORIZATION) Str return response.getBody(); } else { - throw new RuntimeException("Authorization header missing or not using Basic Auth"); + throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "Authorization header missing or not using Basic Auth"); } } }