-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Enable conditional oauth authentication for terraform-boot
- Loading branch information
1 parent
ca7e865
commit df0d71a
Showing
8 changed files
with
371 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
29 changes: 29 additions & 0 deletions
29
src/main/java/org/eclipse/xpanse/terraform/boot/security/oauth2/config/Oauth2Constants.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
/* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
* SPDX-FileCopyrightText: Huawei Inc. | ||
* | ||
*/ | ||
|
||
package org.eclipse.xpanse.terraform.boot.security.oauth2.config; | ||
|
||
/** | ||
* Constants for OAuth2 Authorization. | ||
*/ | ||
public class Oauth2Constants { | ||
|
||
/** | ||
* Auth token type: JWT. | ||
*/ | ||
public static final String AUTH_TYPE_JWT = "JWT"; | ||
|
||
/** | ||
* Auth token type: OpaqueToken. | ||
*/ | ||
public static final String AUTH_TYPE_TOKEN = "OpaqueToken"; | ||
|
||
/** | ||
* Mandatory scope to request the profile of the user. | ||
*/ | ||
public static final String OPENID_SCOPE = "openid"; | ||
|
||
} |
52 changes: 52 additions & 0 deletions
52
...in/java/org/eclipse/xpanse/terraform/boot/security/oauth2/config/Oauth2OpenApiConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
/* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
* SPDX-FileCopyrightText: Huawei Inc. | ||
* | ||
*/ | ||
|
||
package org.eclipse.xpanse.terraform.boot.security.oauth2.config; | ||
|
||
import static org.eclipse.xpanse.terraform.boot.security.oauth2.config.Oauth2Constants.OPENID_SCOPE; | ||
|
||
import io.swagger.v3.oas.annotations.OpenAPIDefinition; | ||
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; | ||
import io.swagger.v3.oas.annotations.info.Info; | ||
import io.swagger.v3.oas.annotations.security.OAuthFlow; | ||
import io.swagger.v3.oas.annotations.security.OAuthFlows; | ||
import io.swagger.v3.oas.annotations.security.OAuthScope; | ||
import io.swagger.v3.oas.annotations.security.SecurityRequirement; | ||
import io.swagger.v3.oas.annotations.security.SecurityScheme; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.context.annotation.Profile; | ||
|
||
|
||
/** | ||
* Configuration springdoc security OAuth2. | ||
*/ | ||
@Profile("oauth") | ||
@OpenAPIDefinition( | ||
info = @Info( | ||
title = "Terraform-Boot API", | ||
description = "RESTful Services to interact with Terraform-Boot runtime", | ||
version = "${app.version}" | ||
), | ||
security = @SecurityRequirement(name = "OAuth2 Flow", | ||
scopes = {OPENID_SCOPE}) | ||
) | ||
@SecurityScheme( | ||
name = "OAuth2 Flow", | ||
type = SecuritySchemeType.OAUTH2, | ||
flows = @OAuthFlows(authorizationCode = | ||
@OAuthFlow( | ||
authorizationUrl = "${springdoc.oAuthFlow.authorizationUrl}", | ||
tokenUrl = "${springdoc.oAuthFlow.tokenUrl}", | ||
scopes = { | ||
@OAuthScope(name = OPENID_SCOPE, | ||
description = "mandatory must be selected.") | ||
} | ||
) | ||
) | ||
) | ||
@Configuration | ||
public class Oauth2OpenApiConfig { | ||
} |
161 changes: 161 additions & 0 deletions
161
...ava/org/eclipse/xpanse/terraform/boot/security/oauth2/config/Oauth2WebSecurityConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
/* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
* SPDX-FileCopyrightText: Huawei Inc. | ||
* | ||
*/ | ||
|
||
package org.eclipse.xpanse.terraform.boot.security.oauth2.config; | ||
|
||
|
||
import static org.eclipse.xpanse.terraform.boot.security.oauth2.config.Oauth2Constants.AUTH_TYPE_JWT; | ||
import static org.eclipse.xpanse.terraform.boot.security.oauth2.config.Oauth2Constants.AUTH_TYPE_TOKEN; | ||
import static org.springframework.web.cors.CorsConfiguration.ALL; | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import java.io.PrintWriter; | ||
import java.time.Duration; | ||
import java.util.Collections; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.apache.commons.lang3.StringUtils; | ||
import org.eclipse.xpanse.terraform.boot.models.response.Response; | ||
import org.eclipse.xpanse.terraform.boot.models.response.ResultType; | ||
import org.eclipse.xpanse.terraform.boot.security.oauth2.introspector.OauthOpaqueTokenIntrospector; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.context.annotation.Profile; | ||
import org.springframework.http.MediaType; | ||
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; | ||
import org.springframework.security.config.annotation.web.builders.HttpSecurity; | ||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; | ||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; | ||
import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator; | ||
import org.springframework.security.oauth2.core.OAuth2TokenValidator; | ||
import org.springframework.security.oauth2.jwt.Jwt; | ||
import org.springframework.security.oauth2.jwt.JwtDecoder; | ||
import org.springframework.security.oauth2.jwt.JwtDecoders; | ||
import org.springframework.security.oauth2.jwt.JwtIssuerValidator; | ||
import org.springframework.security.oauth2.jwt.JwtTimestampValidator; | ||
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; | ||
import org.springframework.security.web.SecurityFilterChain; | ||
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter; | ||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher; | ||
import org.springframework.web.cors.CorsConfiguration; | ||
import org.springframework.web.cors.CorsConfigurationSource; | ||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource; | ||
|
||
/** | ||
* Configuration applied on all web endpoints defined for this | ||
* application. Any configuration on specific resources is applied | ||
* in addition to these global rules. | ||
*/ | ||
@Slf4j | ||
@Profile("oauth") | ||
@Configuration | ||
@EnableWebSecurity | ||
@EnableMethodSecurity(securedEnabled = true) | ||
public class Oauth2WebSecurityConfig { | ||
|
||
@Value("${authorization-token-type:JWT}") | ||
private String authTokenType; | ||
|
||
@Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}") | ||
private String issuerUri; | ||
|
||
@Value("${spring.security.oauth2.resourceserver.opaquetoken.introspection-uri}") | ||
private String introspectionUri; | ||
|
||
@Value("${spring.security.oauth2.resourceserver.opaquetoken.client-id}") | ||
private String clientId; | ||
|
||
@Value("${spring.security.oauth2.resourceserver.opaquetoken.client-secret}") | ||
private String clientSecret; | ||
|
||
/** | ||
* Configures basic security handler per HTTP session. | ||
* | ||
* @param http security configuration | ||
*/ | ||
@Bean | ||
public SecurityFilterChain apiFilterChain(HttpSecurity http) | ||
throws Exception { | ||
// accept cors requests and allow preflight checks | ||
http.cors(httpSecurityCorsConfigurer -> httpSecurityCorsConfigurer.configurationSource( | ||
corsConfigurationSource())); | ||
|
||
http.authorizeHttpRequests(arc -> { | ||
arc.requestMatchers(AntPathRequestMatcher.antMatcher("/swagger-ui/**")).permitAll(); | ||
arc.requestMatchers(AntPathRequestMatcher.antMatcher("/v3/**")).permitAll(); | ||
arc.requestMatchers(AntPathRequestMatcher.antMatcher("/error")).permitAll(); | ||
arc.anyRequest().authenticated(); | ||
}); | ||
|
||
http.csrf(AbstractHttpConfigurer::disable); | ||
|
||
http.headers(headersConfigurer -> headersConfigurer.addHeaderWriter( | ||
new XFrameOptionsHeaderWriter( | ||
XFrameOptionsHeaderWriter.XFrameOptionsMode.SAMEORIGIN))); | ||
|
||
// set custom exception handler | ||
http.exceptionHandling(exceptionHandlingConfigurer -> | ||
exceptionHandlingConfigurer.authenticationEntryPoint( | ||
(httpRequest, httpResponse, authException) -> { | ||
httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); | ||
httpResponse.setCharacterEncoding("UTF-8"); | ||
httpResponse.setContentType(MediaType.APPLICATION_JSON_VALUE); | ||
ObjectMapper objectMapper = new ObjectMapper(); | ||
Response responseModel = Response.errorResponse(ResultType.UNAUTHORIZED, | ||
Collections.singletonList(ResultType.UNAUTHORIZED.toValue())); | ||
String resBody = objectMapper.writeValueAsString(responseModel); | ||
PrintWriter printWriter = httpResponse.getWriter(); | ||
printWriter.print(resBody); | ||
printWriter.flush(); | ||
printWriter.close(); | ||
} | ||
)); | ||
|
||
if (StringUtils.equalsIgnoreCase(AUTH_TYPE_TOKEN, authTokenType)) { | ||
// Config custom OpaqueTokenIntrospector | ||
http.oauth2ResourceServer(oauth2 -> | ||
oauth2.opaqueToken(opaque -> | ||
opaque.introspector( | ||
new OauthOpaqueTokenIntrospector(introspectionUri, | ||
clientId, clientSecret)) | ||
) | ||
); | ||
} | ||
|
||
if (StringUtils.equalsIgnoreCase(AUTH_TYPE_JWT, authTokenType)) { | ||
// Config custom JwtAuthenticationConverter | ||
http.oauth2ResourceServer(oauth2 -> oauth2 | ||
.jwt(jwt -> jwtDecoder()) | ||
); | ||
} | ||
return http.build(); | ||
} | ||
|
||
CorsConfigurationSource corsConfigurationSource() { | ||
CorsConfiguration configuration = new CorsConfiguration(); | ||
configuration.setAllowCredentials(true); | ||
configuration.addAllowedHeader(ALL); | ||
configuration.addAllowedMethod(ALL); | ||
configuration.addAllowedOriginPattern(ALL); | ||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); | ||
source.registerCorsConfiguration("/**", configuration); | ||
return source; | ||
} | ||
|
||
@Bean | ||
@ConditionalOnProperty("authorization-token-type=JWT") | ||
JwtDecoder jwtDecoder() { | ||
NimbusJwtDecoder jwtDecoder = JwtDecoders.fromIssuerLocation(issuerUri); | ||
OAuth2TokenValidator<Jwt> withClockSkew = new DelegatingOAuth2TokenValidator<>( | ||
new JwtTimestampValidator(Duration.ofSeconds(60)), | ||
new JwtIssuerValidator(issuerUri)); | ||
jwtDecoder.setJwtValidator(withClockSkew); | ||
return jwtDecoder; | ||
} | ||
|
||
} |
47 changes: 47 additions & 0 deletions
47
...ipse/xpanse/terraform/boot/security/oauth2/introspector/OauthOpaqueTokenIntrospector.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
/* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
* SPDX-FileCopyrightText: Huawei Inc. | ||
* | ||
*/ | ||
|
||
package org.eclipse.xpanse.terraform.boot.security.oauth2.introspector; | ||
|
||
|
||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal; | ||
import org.springframework.security.oauth2.server.resource.introspection.NimbusOpaqueTokenIntrospector; | ||
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector; | ||
|
||
/** | ||
* Customize the OAuth2AuthoritiesOpaqueTokenIntrospector implements OpaqueTokenIntrospector. | ||
*/ | ||
@Slf4j | ||
public class OauthOpaqueTokenIntrospector implements OpaqueTokenIntrospector { | ||
|
||
private final OpaqueTokenIntrospector opaqueTokenIntrospector; | ||
|
||
/** | ||
* Constructor. | ||
* | ||
* @param introspectionUri The url of IAM server to verify token. | ||
* @param clientId The id of api client created in IAM server. | ||
* @param clientSecret The secret of api client created in IAM server. | ||
*/ | ||
public OauthOpaqueTokenIntrospector(String introspectionUri, | ||
String clientId, | ||
String clientSecret) { | ||
opaqueTokenIntrospector = | ||
new NimbusOpaqueTokenIntrospector(introspectionUri, clientId, clientSecret); | ||
} | ||
|
||
/** | ||
* Verify token. | ||
* | ||
* @param token The token of current user. | ||
* @return OAuth2AuthenticatedPrincipal | ||
*/ | ||
public OAuth2AuthenticatedPrincipal introspect(String token) { | ||
return this.opaqueTokenIntrospector.introspect(token); | ||
} | ||
|
||
} |
Oops, something went wrong.