diff --git a/ebms-provider/src/main/kotlin/no/nav/emottak/ebms/App.kt b/ebms-provider/src/main/kotlin/no/nav/emottak/ebms/App.kt index 7c35bae3..893eb31d 100644 --- a/ebms-provider/src/main/kotlin/no/nav/emottak/ebms/App.kt +++ b/ebms-provider/src/main/kotlin/no/nav/emottak/ebms/App.kt @@ -3,20 +3,8 @@ */ package no.nav.emottak.ebms -import com.nimbusds.jwt.SignedJWT import dev.reformator.stacktracedecoroutinator.runtime.DecoroutinatorRuntime -import io.ktor.client.HttpClient -import io.ktor.client.engine.cio.CIO -import io.ktor.client.plugins.auth.Auth -import io.ktor.client.plugins.auth.providers.BearerTokens -import io.ktor.client.plugins.auth.providers.bearer -import io.ktor.client.plugins.contentnegotiation.ContentNegotiation import io.ktor.client.request.forms.MultiPartFormDataContent -import io.ktor.client.request.header -import io.ktor.client.request.headers -import io.ktor.client.request.post -import io.ktor.client.request.setBody -import io.ktor.client.statement.bodyAsText import io.ktor.http.ContentType import io.ktor.http.Headers import io.ktor.http.HeadersBuilder @@ -24,7 +12,6 @@ import io.ktor.http.HttpStatusCode import io.ktor.http.content.PartData import io.ktor.http.content.forEachPart import io.ktor.http.content.streamProvider -import io.ktor.serialization.kotlinx.json.json import io.ktor.server.application.Application import io.ktor.server.application.ApplicationCall import io.ktor.server.application.call @@ -47,7 +34,6 @@ import io.micrometer.prometheus.PrometheusConfig import io.micrometer.prometheus.PrometheusMeterRegistry import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext -import kotlinx.serialization.json.Json import net.logstash.logback.marker.Markers import no.nav.emottak.constants.SMTPHeaders import no.nav.emottak.ebms.model.DokumentType @@ -66,12 +52,10 @@ import no.nav.emottak.util.createUniqueMimeMessageId import no.nav.emottak.util.getEnvVar import org.slf4j.LoggerFactory import java.io.ByteArrayInputStream -import java.net.InetSocketAddress -import java.net.Proxy -import java.net.URL import java.time.Duration import java.time.Instant -import java.util.* +import java.util.Base64 +import java.util.UUID import kotlin.time.toKotlinDuration val log = LoggerFactory.getLogger("no.nav.emottak.ebms.App") @@ -89,83 +73,6 @@ fun main() { }).start(wait = true) } -fun defaultHttpClient(): () -> HttpClient { - return { - HttpClient(CIO) { - expectSuccess = true - install(io.ktor.client.plugins.contentnegotiation.ContentNegotiation) { - json() - } - } - } -} - -val EBMS_SEND_IN_SCOPE = getEnvVar( - "EBMS_SEND_IN_SCOPE", - "api://" + getEnvVar("NAIS_CLUSTER_NAME", "dev-fss") + ".team-emottak.ebms-send-in/.default" -) -const val AZURE_AD_AUTH = "AZURE_AD" -val LENIENT_JSON_PARSER = Json { - isLenient = true -} -fun sendInAuthHttpClient(): () -> HttpClient { - return { - HttpClient(CIO) { - install(ContentNegotiation) { - json() - } - install(Auth) { - bearer { - refreshTokens { - val clientId = getEnvVar("AZURE_APP_CLIENT_ID", "ebms-send-in") - val azureEndpoint = getEnvVar( - "AZURE_OPENID_CONFIG_TOKEN_ENDPOINT", - "http://localhost:3344/$AZURE_AD_AUTH/token" - ) - val requestBody = - "client_id=" + clientId + - "&client_secret=" + getEnvVar("AZURE_APP_CLIENT_SECRET", "dummysecret") + - "&scope=" + EBMS_SEND_IN_SCOPE + - "&grant_type=client_credentials" - // log.info("sendInAuthHttpClient() -> refreshTokens: client_id: $clientId, scope: $EBMS_SEND_IN_SCOPE, doing a post request to $azureEndpoint") - HttpClient(CIO) { - engine { - val httpProxyUrl = getEnvVar("HTTP_PROXY", "") - if (httpProxyUrl.isNotBlank()) { - proxy = Proxy( - Proxy.Type.HTTP, - InetSocketAddress(URL(httpProxyUrl).host, URL(httpProxyUrl).port) - ) - } - } - }.post( - azureEndpoint - ) { - headers { - header("Content-Type", "application/x-www-form-urlencoded") - } - setBody(requestBody) - }.bodyAsText() - .let { tokenResponseString -> - // log.info("The token response string we received was: $tokenResponseString") - SignedJWT.parse( - LENIENT_JSON_PARSER.decodeFromString>(tokenResponseString)["access_token"] as String - ) - } - .let { parsedJwt -> - // log.info("After parsing it, we got: $parsedJwt") - BearerTokens(parsedJwt.serialize(), "refresh token is unused") - } - } - sendWithoutRequest { - true - } - } - } - } - } -} - fun PartData.payload(clearText: Boolean = false): ByteArray { return when (this) { is PartData.FormItem -> if (clearText) { @@ -185,13 +92,13 @@ fun PartData.payload(clearText: Boolean = false): ByteArray { } fun Application.ebmsProviderModule() { - val client = defaultHttpClient() - val cpaClient = CpaRepoClient(client) - val processingClient = PayloadProcessingClient(client) - val sendInHttpClient = sendInAuthHttpClient() - val sendInClient = SendInClient(sendInHttpClient) + val cpaClient = CpaRepoClient(defaultHttpClient()) val validator = DokumentValidator(cpaClient) + + val processingClient = PayloadProcessingClient(scopedAuthHttpClient(EBMS_PAYLOAD_SCOPE)) val processing = ProcessingService(processingClient) + + val sendInClient = SendInClient(scopedAuthHttpClient(EBMS_SEND_IN_SCOPE)) val sendInService = SendInService(sendInClient) val appMicrometerRegistry = PrometheusMeterRegistry(PrometheusConfig.DEFAULT) diff --git a/ebms-provider/src/main/kotlin/no/nav/emottak/ebms/HttpClients.kt b/ebms-provider/src/main/kotlin/no/nav/emottak/ebms/HttpClients.kt index 03e473e5..2e1a11d8 100644 --- a/ebms-provider/src/main/kotlin/no/nav/emottak/ebms/HttpClients.kt +++ b/ebms-provider/src/main/kotlin/no/nav/emottak/ebms/HttpClients.kt @@ -1,11 +1,22 @@ package no.nav.emottak.ebms +import com.nimbusds.jwt.SignedJWT import io.ktor.client.HttpClient import io.ktor.client.call.body +import io.ktor.client.engine.cio.CIO +import io.ktor.client.plugins.auth.Auth +import io.ktor.client.plugins.auth.providers.BearerTokens +import io.ktor.client.plugins.auth.providers.bearer +import io.ktor.client.plugins.contentnegotiation.ContentNegotiation +import io.ktor.client.request.header +import io.ktor.client.request.headers import io.ktor.client.request.post import io.ktor.client.request.setBody +import io.ktor.client.statement.bodyAsText import io.ktor.http.ContentType import io.ktor.http.contentType +import io.ktor.serialization.kotlinx.json.json +import kotlinx.serialization.json.Json import no.nav.emottak.melding.model.PayloadRequest import no.nav.emottak.melding.model.PayloadResponse import no.nav.emottak.melding.model.SendInRequest @@ -13,6 +24,9 @@ import no.nav.emottak.melding.model.SendInResponse import no.nav.emottak.melding.model.ValidationRequest import no.nav.emottak.melding.model.ValidationResult import no.nav.emottak.util.getEnvVar +import java.net.InetSocketAddress +import java.net.Proxy +import java.net.URI class CpaRepoClient(clientProvider: () -> HttpClient) { private var httpClient = clientProvider.invoke() @@ -50,3 +64,87 @@ class SendInClient(clientProvider: () -> HttpClient) { }.body() } } + +val EBMS_SEND_IN_SCOPE = getEnvVar( + "EBMS_SEND_IN_SCOPE", + "api://" + getEnvVar("NAIS_CLUSTER_NAME", "dev-fss") + ".team-emottak.ebms-send-in/.default" +) +val EBMS_PAYLOAD_SCOPE = getEnvVar( + "EBMS_PAYLOAD_SCOPE", + "api://" + getEnvVar("NAIS_CLUSTER_NAME", "dev-fss") + ".team-emottak.ebms-payload/.default" +) + +fun defaultHttpClient(): () -> HttpClient { + return { + HttpClient(CIO) { + expectSuccess = true + install(ContentNegotiation) { + json() + } + } + } +} + +const val AZURE_AD_AUTH = "AZURE_AD" +val LENIENT_JSON_PARSER = Json { + isLenient = true +} + +fun scopedAuthHttpClient( + scope: String +): () -> HttpClient { + return { + HttpClient(CIO) { + install(ContentNegotiation) { + json() + } + install(Auth) { + bearer { + refreshTokens { + proxiedHttpClient().post( + getEnvVar( + "AZURE_OPENID_CONFIG_TOKEN_ENDPOINT", + "http://localhost:3344/$AZURE_AD_AUTH/token" + ) + ) { + headers { + header("Content-Type", "application/x-www-form-urlencoded") + } + setBody( + "client_id=" + getEnvVar("AZURE_APP_CLIENT_ID", "dummyclient") + + "&client_secret=" + getEnvVar("AZURE_APP_CLIENT_SECRET", "dummysecret") + + "&scope=" + scope + + "&grant_type=client_credentials" + ) + }.bodyAsText() + .let { tokenResponseString -> + // log.info("The token response string we received was: $tokenResponseString") + SignedJWT.parse( + LENIENT_JSON_PARSER.decodeFromString>(tokenResponseString)["access_token"] as String + ) + } + .let { parsedJwt -> + // log.info("After parsing it, we got: $parsedJwt") + BearerTokens(parsedJwt.serialize(), "refresh token is unused") + } + } + sendWithoutRequest { + true + } + } + } + } + } +} + +private fun proxiedHttpClient() = HttpClient(CIO) { + engine { + val httpProxyUrl = getEnvVar("HTTP_PROXY", "") + if (httpProxyUrl.isNotBlank()) { + proxy = Proxy( + Proxy.Type.HTTP, + InetSocketAddress(URI(httpProxyUrl).toURL().host, URI(httpProxyUrl).toURL().port) + ) + } + } +}