From 72d9d2895ef8d7c40c34aeb3d219e32090985c91 Mon Sep 17 00:00:00 2001 From: Johan Blomgren Date: Mon, 12 Feb 2024 13:51:53 +0100 Subject: [PATCH] =?UTF-8?q?Justert=20libs=20til=20ny=20versjon=20av=20toke?= =?UTF-8?q?n-support=20Fjernet=20retry=20p=C3=A5=20502=20d=C3=A5=20det=20i?= =?UTF-8?q?kke=20er=20rett=20frem=20og=20enligt=20loggene=20er=20det=20kun?= =?UTF-8?q?=20timeouts=20siste=203=20mnd?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../http/client/ProblemDetailException.kt | 2 +- .../libs/http/client/RetryOAuth2HttpClient.kt | 10 +++---- .../http/config/RestTemplateConfiguration.kt | 23 +++++++++------- .../BearerTokenClientInterceptor.kt | 22 +++++++++------- .../http/client/RetryOAuth2HttpClientTest.kt | 26 +++++++++---------- ...nClientCredentialsClientInterceptorTest.kt | 4 +-- .../BearerTokenClientInterceptorTest.kt | 4 +-- ...rerTokenOnBehalfOfClientInterceptorTest.kt | 4 +-- .../interceptor/ClientConfigTestProperties.kt | 10 +++---- .../EksternBrukerUtils.kt | 3 ++- 10 files changed, 56 insertions(+), 52 deletions(-) diff --git a/http-client/main/no/nav/tilleggsstonader/libs/http/client/ProblemDetailException.kt b/http-client/main/no/nav/tilleggsstonader/libs/http/client/ProblemDetailException.kt index bc55344..08aade5 100644 --- a/http-client/main/no/nav/tilleggsstonader/libs/http/client/ProblemDetailException.kt +++ b/http-client/main/no/nav/tilleggsstonader/libs/http/client/ProblemDetailException.kt @@ -7,5 +7,5 @@ import org.springframework.web.client.RestClientResponseException class ProblemDetailException( val detail: ProblemDetail, val responseException: RestClientResponseException, - val httpStatus: HttpStatus = HttpStatus.valueOf(responseException.rawStatusCode), + val httpStatus: HttpStatus = HttpStatus.valueOf(responseException.statusCode.value()), ) : RuntimeException(responseException) diff --git a/http-client/main/no/nav/tilleggsstonader/libs/http/client/RetryOAuth2HttpClient.kt b/http-client/main/no/nav/tilleggsstonader/libs/http/client/RetryOAuth2HttpClient.kt index 5e27348..5fb507f 100644 --- a/http-client/main/no/nav/tilleggsstonader/libs/http/client/RetryOAuth2HttpClient.kt +++ b/http-client/main/no/nav/tilleggsstonader/libs/http/client/RetryOAuth2HttpClient.kt @@ -4,16 +4,16 @@ import no.nav.security.token.support.client.core.http.OAuth2HttpRequest import no.nav.security.token.support.client.core.oauth2.OAuth2AccessTokenResponse import no.nav.security.token.support.client.spring.oauth2.DefaultOAuth2HttpClient import org.slf4j.LoggerFactory -import org.springframework.boot.web.client.RestTemplateBuilder import org.springframework.core.NestedExceptionUtils import org.springframework.web.client.HttpServerErrorException +import org.springframework.web.client.RestClient import java.net.SocketException import java.net.SocketTimeoutException class RetryOAuth2HttpClient( - restTemplateBuilder: RestTemplateBuilder, + restClient: RestClient, private val maxRetries: Int = 2, -) : DefaultOAuth2HttpClient(restTemplateBuilder) { +) : DefaultOAuth2HttpClient(restClient) { private val logger = LoggerFactory.getLogger(javaClass) private val secureLogger = LoggerFactory.getLogger("secureLogger") @@ -23,10 +23,9 @@ class RetryOAuth2HttpClient( SocketException::class, SocketTimeoutException::class, HttpServerErrorException.GatewayTimeout::class, - HttpServerErrorException.BadGateway::class, ) - override fun post(req: OAuth2HttpRequest): OAuth2AccessTokenResponse? { + override fun post(req: OAuth2HttpRequest): OAuth2AccessTokenResponse { var retries = 0 while (true) { @@ -44,6 +43,7 @@ class RetryOAuth2HttpClient( retries: Int, oAuth2HttpRequest: OAuth2HttpRequest, ) { + e.printStackTrace() if (shouldRetry(e) && retries < maxRetries) { logger.warn( "Kall mot url=${oAuth2HttpRequest.tokenEndpointUrl} feilet, cause=${ diff --git a/http-client/main/no/nav/tilleggsstonader/libs/http/config/RestTemplateConfiguration.kt b/http-client/main/no/nav/tilleggsstonader/libs/http/config/RestTemplateConfiguration.kt index 340b503..752d1d7 100644 --- a/http-client/main/no/nav/tilleggsstonader/libs/http/config/RestTemplateConfiguration.kt +++ b/http-client/main/no/nav/tilleggsstonader/libs/http/config/RestTemplateConfiguration.kt @@ -7,11 +7,14 @@ import no.nav.tilleggsstonader.libs.http.interceptor.BearerTokenExchangeClientIn import no.nav.tilleggsstonader.libs.http.interceptor.BearerTokenOnBehalfOfClientInterceptor import no.nav.tilleggsstonader.libs.http.interceptor.ConsumerIdClientInterceptor import no.nav.tilleggsstonader.libs.http.interceptor.MdcValuesPropagatingClientInterceptor +import org.springframework.boot.web.client.ClientHttpRequestFactories +import org.springframework.boot.web.client.ClientHttpRequestFactorySettings import org.springframework.boot.web.client.RestTemplateBuilder import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.context.annotation.Import import org.springframework.context.annotation.Primary +import org.springframework.web.client.RestClient import org.springframework.web.client.RestTemplate import java.time.Duration import java.time.temporal.ChronoUnit @@ -34,19 +37,19 @@ class RestTemplateConfiguration( @Primary @Bean fun oAuth2HttpClient( - restTemplateBuilder: RestTemplateBuilder, + restClientBuilder: RestClient.Builder, consumerIdClientInterceptor: ConsumerIdClientInterceptor, mdcValuesPropagatingClientInterceptor: MdcValuesPropagatingClientInterceptor, ): RetryOAuth2HttpClient { - return RetryOAuth2HttpClient( - restTemplateBuilder - .setConnectTimeout(Duration.of(2, ChronoUnit.SECONDS)) - .setReadTimeout(Duration.of(4, ChronoUnit.SECONDS)) - .additionalInterceptors( - consumerIdClientInterceptor, - mdcValuesPropagatingClientInterceptor, - ), - ) + val clientHttpRequestFactorySettings = ClientHttpRequestFactorySettings.DEFAULTS + .withConnectTimeout(Duration.of(1, ChronoUnit.SECONDS)) + .withReadTimeout(Duration.of(1, ChronoUnit.SECONDS)) + val restClient = restClientBuilder + .requestFactory(ClientHttpRequestFactories.get(clientHttpRequestFactorySettings)) + .requestInterceptor(consumerIdClientInterceptor) + .requestInterceptor(mdcValuesPropagatingClientInterceptor) + .build() + return RetryOAuth2HttpClient(restClient) } @Bean("utenAuth") diff --git a/http-client/main/no/nav/tilleggsstonader/libs/http/interceptor/BearerTokenClientInterceptor.kt b/http-client/main/no/nav/tilleggsstonader/libs/http/interceptor/BearerTokenClientInterceptor.kt index dbf3e51..dcfee38 100644 --- a/http-client/main/no/nav/tilleggsstonader/libs/http/interceptor/BearerTokenClientInterceptor.kt +++ b/http-client/main/no/nav/tilleggsstonader/libs/http/interceptor/BearerTokenClientInterceptor.kt @@ -1,9 +1,10 @@ package no.nav.tilleggsstonader.libs.http.interceptor +import com.nimbusds.oauth2.sdk.GrantType import no.nav.security.token.support.client.core.ClientProperties -import no.nav.security.token.support.client.core.OAuth2GrantType import no.nav.security.token.support.client.core.oauth2.OAuth2AccessTokenService import no.nav.security.token.support.client.spring.ClientConfigurationProperties +import no.nav.security.token.support.core.exceptions.JwtTokenMissingException import no.nav.security.token.support.spring.SpringTokenValidationContextHolder import org.springframework.http.HttpRequest import org.springframework.http.client.ClientHttpRequestExecution @@ -50,7 +51,7 @@ class BearerTokenClientCredentialsClientInterceptor( request, clientConfigurationProperties, oAuth2AccessTokenService, - OAuth2GrantType.CLIENT_CREDENTIALS, + GrantType.CLIENT_CREDENTIALS, ), ) return execution.execute(request, body) @@ -73,7 +74,7 @@ class BearerTokenExchangeClientInterceptor( request, clientConfigurationProperties, oAuth2AccessTokenService, - OAuth2GrantType.TOKEN_EXCHANGE, + GrantType.TOKEN_EXCHANGE, ), ) return execution.execute(request, body) @@ -96,7 +97,7 @@ class BearerTokenOnBehalfOfClientInterceptor( request, clientConfigurationProperties, oAuth2AccessTokenService, - OAuth2GrantType.JWT_BEARER, + GrantType.JWT_BEARER, ), ) return execution.execute(request, body) @@ -107,7 +108,7 @@ private fun genererAccessToken( request: HttpRequest, clientConfigurationProperties: ClientConfigurationProperties, oAuth2AccessTokenService: OAuth2AccessTokenService, - grantType: OAuth2GrantType? = null, + grantType: GrantType? = null, ): String { val clientProperties = clientPropertiesFor( request.uri, @@ -115,6 +116,7 @@ private fun genererAccessToken( grantType, ) return oAuth2AccessTokenService.getAccessToken(clientProperties).accessToken + ?: throw JwtTokenMissingException() } /** @@ -127,7 +129,7 @@ private fun genererAccessToken( private fun clientPropertiesFor( uri: URI, clientConfigurationProperties: ClientConfigurationProperties, - grantType: OAuth2GrantType?, + grantType: GrantType?, ): ClientProperties { val clientProperties = filterClientProperties(clientConfigurationProperties, uri) return if (grantType == null) { @@ -151,7 +153,7 @@ private fun filterClientProperties( private fun clientPropertiesForGrantType( values: List, - grantType: OAuth2GrantType, + grantType: GrantType, uri: URI, ): ClientProperties { return values.firstOrNull { grantType == it.grantType } @@ -159,12 +161,12 @@ private fun clientPropertiesForGrantType( } private fun clientCredentialOrJwtBearer() = - if (erSystembruker()) OAuth2GrantType.CLIENT_CREDENTIALS else OAuth2GrantType.JWT_BEARER + if (erSystembruker()) GrantType.CLIENT_CREDENTIALS else GrantType.JWT_BEARER private fun erSystembruker(): Boolean { return try { - val preferred_username = - SpringTokenValidationContextHolder().tokenValidationContext.getClaims("azuread")["preferred_username"] + val tokenValidationContext = SpringTokenValidationContextHolder().getTokenValidationContext() + val preferred_username = tokenValidationContext.getClaims("azuread").get("preferred_username") return preferred_username == null } catch (e: Throwable) { // Ingen request context. Skjer ved kall som har opphav i kjørende applikasjon. Ping etc. diff --git a/http-client/test/no/nav/tilleggsstonader/libs/http/client/RetryOAuth2HttpClientTest.kt b/http-client/test/no/nav/tilleggsstonader/libs/http/client/RetryOAuth2HttpClientTest.kt index ae6dd20..96e170c 100644 --- a/http-client/test/no/nav/tilleggsstonader/libs/http/client/RetryOAuth2HttpClientTest.kt +++ b/http-client/test/no/nav/tilleggsstonader/libs/http/client/RetryOAuth2HttpClientTest.kt @@ -12,18 +12,24 @@ import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test -import org.springframework.boot.web.client.RestTemplateBuilder +import org.springframework.boot.web.client.ClientHttpRequestFactories +import org.springframework.boot.web.client.ClientHttpRequestFactorySettings +import org.springframework.web.client.RestClient import java.net.URI import java.time.Duration import java.time.temporal.ChronoUnit internal class RetryOAuth2HttpClientTest { - private val restTemplateBuilder = RestTemplateBuilder() - .setConnectTimeout(Duration.of(1, ChronoUnit.SECONDS)) - .setReadTimeout(Duration.of(1, ChronoUnit.SECONDS)) + val clientHttpRequestFactorySettings = ClientHttpRequestFactorySettings.DEFAULTS + .withConnectTimeout(Duration.of(1, ChronoUnit.SECONDS)) + .withReadTimeout(Duration.of(1, ChronoUnit.SECONDS)) - private val client = RetryOAuth2HttpClient(restTemplateBuilder) + val requestFactory = ClientHttpRequestFactories.get(clientHttpRequestFactorySettings) + val restClient = RestClient.builder() + .requestFactory(requestFactory) + .build() + val client = RetryOAuth2HttpClient(restClient) @BeforeEach internal fun setUp() { @@ -51,13 +57,6 @@ internal class RetryOAuth2HttpClientTest { wireMockServer.verify(2, RequestPatternBuilder.allRequests()) } - @Test - internal fun `502 - skal prøve på nytt`() { - stub(WireMock.serverError().withStatus(502)) - post() - wireMockServer.verify(3, RequestPatternBuilder.allRequests()) - } - @Test internal fun `socketException - skal prøve på nytt`() { stub(WireMock.serverError().withFault(Fault.CONNECTION_RESET_BY_PEER)) @@ -82,8 +81,7 @@ internal class RetryOAuth2HttpClientTest { private fun post(): Exception? { return try { client.post( - OAuth2HttpRequest.builder() - .tokenEndpointUrl(URI.create(wireMockServer.baseUrl())) + OAuth2HttpRequest.builder(URI.create(wireMockServer.baseUrl())) .oAuth2HttpHeaders(OAuth2HttpHeaders.builder().build()) .build(), ) diff --git a/http-client/test/no/nav/tilleggsstonader/libs/http/interceptor/BearerTokenClientCredentialsClientInterceptorTest.kt b/http-client/test/no/nav/tilleggsstonader/libs/http/interceptor/BearerTokenClientCredentialsClientInterceptorTest.kt index d2e0100..298c27d 100644 --- a/http-client/test/no/nav/tilleggsstonader/libs/http/interceptor/BearerTokenClientCredentialsClientInterceptorTest.kt +++ b/http-client/test/no/nav/tilleggsstonader/libs/http/interceptor/BearerTokenClientCredentialsClientInterceptorTest.kt @@ -34,7 +34,7 @@ internal class BearerTokenClientCredentialsClientInterceptorTest { bearerTokenClientInterceptor.intercept(req, ByteArray(0), execution) - verify { oAuth2AccessTokenService.getAccessToken(clientConfigurationProperties.registration["1"]) } + verify { oAuth2AccessTokenService.getAccessToken(clientConfigurationProperties.registration["1"]!!) } } @Test @@ -43,6 +43,6 @@ internal class BearerTokenClientCredentialsClientInterceptorTest { every { req.uri } returns (URI("http://jwtResource.no")) val execution = mockk(relaxed = true) assertThat(catchThrowable { bearerTokenClientInterceptor.intercept(req, ByteArray(0), execution) }) - .hasMessage("could not find oauth2 client config for uri=http://jwtResource.no and grant type=OAuth2GrantType[value=client_credentials]") + .hasMessage("could not find oauth2 client config for uri=http://jwtResource.no and grant type=client_credentials") } } diff --git a/http-client/test/no/nav/tilleggsstonader/libs/http/interceptor/BearerTokenClientInterceptorTest.kt b/http-client/test/no/nav/tilleggsstonader/libs/http/interceptor/BearerTokenClientInterceptorTest.kt index d86c15e..bdab969 100644 --- a/http-client/test/no/nav/tilleggsstonader/libs/http/interceptor/BearerTokenClientInterceptorTest.kt +++ b/http-client/test/no/nav/tilleggsstonader/libs/http/interceptor/BearerTokenClientInterceptorTest.kt @@ -43,7 +43,7 @@ class BearerTokenClientInterceptorTest { bearerTokenClientInterceptor.intercept(req, ByteArray(0), execution) - verify { oAuth2AccessTokenService.getAccessToken(clientConfigurationProperties.registration["1"]) } + verify { oAuth2AccessTokenService.getAccessToken(clientConfigurationProperties.registration["1"]!!) } } @Test @@ -56,7 +56,7 @@ class BearerTokenClientInterceptorTest { bearerTokenClientInterceptor.intercept(req, ByteArray(0), execution) - verify { oAuth2AccessTokenService.getAccessToken(clientConfigurationProperties.registration["2"]) } + verify { oAuth2AccessTokenService.getAccessToken(clientConfigurationProperties.registration["2"]!!) } } fun mockBrukerContext(preferredUsername: String) { diff --git a/http-client/test/no/nav/tilleggsstonader/libs/http/interceptor/BearerTokenOnBehalfOfClientInterceptorTest.kt b/http-client/test/no/nav/tilleggsstonader/libs/http/interceptor/BearerTokenOnBehalfOfClientInterceptorTest.kt index 0450dcb..84dd11b 100644 --- a/http-client/test/no/nav/tilleggsstonader/libs/http/interceptor/BearerTokenOnBehalfOfClientInterceptorTest.kt +++ b/http-client/test/no/nav/tilleggsstonader/libs/http/interceptor/BearerTokenOnBehalfOfClientInterceptorTest.kt @@ -33,7 +33,7 @@ internal class BearerTokenOnBehalfOfClientInterceptorTest { bearerTokenClientInterceptor.intercept(req, ByteArray(0), execution) - verify { oAuth2AccessTokenService.getAccessToken(clientConfigurationProperties.registration["2"]) } + verify { oAuth2AccessTokenService.getAccessToken(clientConfigurationProperties.registration["2"]!!) } } @Test @@ -50,6 +50,6 @@ internal class BearerTokenOnBehalfOfClientInterceptorTest { ) }, ) - .hasMessage("could not find oauth2 client config for uri=http://clientResource.no and grant type=OAuth2GrantType[value=urn:ietf:params:oauth:grant-type:jwt-bearer]") + .hasMessage("could not find oauth2 client config for uri=http://clientResource.no and grant type=urn:ietf:params:oauth:grant-type:jwt-bearer") } } diff --git a/http-client/test/no/nav/tilleggsstonader/libs/http/interceptor/ClientConfigTestProperties.kt b/http-client/test/no/nav/tilleggsstonader/libs/http/interceptor/ClientConfigTestProperties.kt index 8548fae..9c319ec 100644 --- a/http-client/test/no/nav/tilleggsstonader/libs/http/interceptor/ClientConfigTestProperties.kt +++ b/http-client/test/no/nav/tilleggsstonader/libs/http/interceptor/ClientConfigTestProperties.kt @@ -1,9 +1,9 @@ package no.nav.tilleggsstonader.libs.http.interceptor +import com.nimbusds.oauth2.sdk.GrantType import com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod import no.nav.security.token.support.client.core.ClientAuthenticationProperties import no.nav.security.token.support.client.core.ClientProperties -import no.nav.security.token.support.client.core.OAuth2GrantType import no.nav.security.token.support.client.spring.ClientConfigurationProperties import java.net.URI @@ -20,7 +20,7 @@ val clientConfigurationProperties = "1" to ClientProperties( URI(tokenEndpoint), URI(tokenEndpoint), - OAuth2GrantType.CLIENT_CREDENTIALS, + GrantType.CLIENT_CREDENTIALS, listOf("z", "y", "x"), authentication, URI("http://firstResource.no"), @@ -29,7 +29,7 @@ val clientConfigurationProperties = "2" to ClientProperties( URI(tokenEndpoint), URI(tokenEndpoint), - OAuth2GrantType.JWT_BEARER, + GrantType.JWT_BEARER, listOf("c", "b", "a"), authentication, URI("http://firstResource.no"), @@ -38,7 +38,7 @@ val clientConfigurationProperties = "3" to ClientProperties( URI(tokenEndpoint), URI(tokenEndpoint), - OAuth2GrantType.JWT_BEARER, + GrantType.JWT_BEARER, listOf("z", "y", "x"), authentication, URI("http://jwtResource.no"), @@ -47,7 +47,7 @@ val clientConfigurationProperties = "4" to ClientProperties( URI(tokenEndpoint), URI(tokenEndpoint), - OAuth2GrantType.CLIENT_CREDENTIALS, + GrantType.CLIENT_CREDENTIALS, listOf("z", "y", "x"), authentication, URI("http://clientResource.no"), diff --git a/sikkerhet/main/no.nav.tilleggsstonader.libs.sikkerhet/EksternBrukerUtils.kt b/sikkerhet/main/no.nav.tilleggsstonader.libs.sikkerhet/EksternBrukerUtils.kt index 680bb44..957c799 100644 --- a/sikkerhet/main/no.nav.tilleggsstonader.libs.sikkerhet/EksternBrukerUtils.kt +++ b/sikkerhet/main/no.nav.tilleggsstonader.libs.sikkerhet/EksternBrukerUtils.kt @@ -1,6 +1,7 @@ package no.nav.tilleggsstonader.libs.sikkerhet import no.nav.security.token.support.core.context.TokenValidationContext +import no.nav.security.token.support.core.exceptions.JwtTokenMissingException import no.nav.security.token.support.core.exceptions.JwtTokenValidatorException import no.nav.security.token.support.core.jwt.JwtTokenClaims import no.nav.security.token.support.spring.SpringTokenValidationContextHolder @@ -29,7 +30,7 @@ object EksternBrukerUtils { fun getBearerTokenForLoggedInUser(): String { return getFromContext { validationContext, issuer -> - validationContext.getJwtToken(issuer).tokenAsString + validationContext.getJwtToken(issuer)?.encodedToken ?: throw JwtTokenMissingException() } }