-
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.
test ny altinn-tilganger tjeneste i dev
- Loading branch information
Showing
6 changed files
with
206 additions
and
56 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
89 changes: 89 additions & 0 deletions
89
...ain/kotlin/no/nav/arbeidsgiver/notifikasjon/infrastruktur/altinn/AltinnTilgangerClient.kt
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,89 @@ | ||
package no.nav.arbeidsgiver.notifikasjon.infrastruktur.altinn | ||
|
||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties | ||
import io.ktor.client.* | ||
import io.ktor.client.call.* | ||
import io.ktor.client.engine.* | ||
import io.ktor.client.engine.apache.* | ||
import io.ktor.client.plugins.* | ||
import io.ktor.client.plugins.contentnegotiation.* | ||
import io.ktor.client.request.* | ||
import io.ktor.http.* | ||
import io.ktor.network.sockets.* | ||
import io.ktor.serialization.jackson.* | ||
import no.nav.arbeidsgiver.notifikasjon.bruker.BrukerModel | ||
import no.nav.arbeidsgiver.notifikasjon.infrastruktur.HttpClientMetricsFeature | ||
import no.nav.arbeidsgiver.notifikasjon.infrastruktur.Metrics | ||
import no.nav.arbeidsgiver.notifikasjon.infrastruktur.NaisEnvironment | ||
import no.nav.arbeidsgiver.notifikasjon.infrastruktur.PropagateFromMDCPlugin | ||
import no.nav.arbeidsgiver.notifikasjon.infrastruktur.tokenx.TokenXClient | ||
import no.nav.arbeidsgiver.notifikasjon.infrastruktur.tokenx.TokenXClientImpl | ||
import no.nav.arbeidsgiver.notifikasjon.infrastruktur.tokenx.TokenXPlugin | ||
import org.apache.http.ConnectionClosedException | ||
import javax.net.ssl.SSLHandshakeException | ||
|
||
class AltinnTilgangerClient( | ||
private val baseUrl: String? = null, | ||
private val tokenXClient: TokenXClient = TokenXClientImpl(), | ||
engine: HttpClientEngine = Apache.create(), | ||
) { | ||
|
||
private val httpClient = HttpClient(engine) { | ||
defaultRequest { | ||
url(baseUrl ?: "http://arbeidsgiver-altinn-tilganger") | ||
} | ||
install(ContentNegotiation) { | ||
jackson() | ||
} | ||
install(PropagateFromMDCPlugin) { | ||
propagate("x_correlation_id") | ||
} | ||
install(HttpClientMetricsFeature) { | ||
registry = Metrics.meterRegistry | ||
} | ||
install(HttpRequestRetry) { | ||
maxRetries = 3 | ||
retryOnExceptionIf { _, cause -> | ||
cause is ConnectionClosedException || | ||
cause is SocketTimeoutException || | ||
cause is SSLHandshakeException | ||
} | ||
delayMillis { 250L } | ||
} | ||
install(TokenXPlugin) { | ||
audience = "${NaisEnvironment.clusterName}:fager:arbeidsgiver-altinn-tilganger" | ||
tokenXClient = this@AltinnTilgangerClient.tokenXClient | ||
} | ||
} | ||
|
||
// TODO: ikke bruk BrukerModel typen her, lag egne DTOer for denne klienten og konverter til BrukerModel i tjenesten | ||
suspend fun hentTilganger(): BrukerModel.Tilganger { | ||
val response = httpClient.post { | ||
url { | ||
path("/tilganger") | ||
} | ||
accept(ContentType.Application.Json) | ||
} | ||
val dto = response.body<AltinnTilgangerResponse>() | ||
|
||
|
||
return BrukerModel.Tilganger( | ||
harFeil = dto.isError, | ||
tjenestetilganger = dto.orgNrTilTilganger.flatMap { | ||
(orgNr, tilganger) -> tilganger.map { tilgang -> | ||
val (code, edition) = tilgang.split(":").let { | ||
it.first() to it.getOrElse(1) { "" } | ||
} | ||
BrukerModel.Tilgang.Altinn(orgNr, code, edition) | ||
} | ||
} | ||
) | ||
} | ||
|
||
} | ||
|
||
@JsonIgnoreProperties(ignoreUnknown = true) | ||
data class AltinnTilgangerResponse( | ||
val isError: Boolean, | ||
val orgNrTilTilganger: Map<String, List<String>>, | ||
) |
75 changes: 22 additions & 53 deletions
75
app/src/main/kotlin/no/nav/arbeidsgiver/notifikasjon/infrastruktur/tokenx/TokenXPlugin.kt
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 |
---|---|---|
@@ -1,69 +1,38 @@ | ||
package no.nav.arbeidsgiver.notifikasjon.infrastruktur.tokenx | ||
|
||
import io.ktor.client.* | ||
import io.ktor.client.plugins.* | ||
import io.ktor.client.plugins.api.* | ||
import io.ktor.client.request.* | ||
import io.ktor.http.* | ||
import io.ktor.util.* | ||
import no.nav.arbeidsgiver.notifikasjon.infrastruktur.logger | ||
|
||
private const val BEARER_PREFIX = "Bearer " | ||
|
||
class TokenXPlugin internal constructor( | ||
val audience: String, | ||
val tokenXClient: TokenXClient, | ||
) { | ||
class TokenXPluginConfig { | ||
var audience: String? = null | ||
var tokenXClient: TokenXClient? = null | ||
} | ||
|
||
class Config { | ||
var audience: String? = null | ||
set(value) { | ||
require(audience == null) { | ||
"audience already set" | ||
} | ||
field = value | ||
} | ||
var tokenXClient: TokenXClient? = null | ||
set(value) { | ||
require(tokenXClient == null) { | ||
"tokenXClient already set" | ||
} | ||
field = value | ||
} | ||
} | ||
|
||
companion object Plugin : HttpClientPlugin<Config, TokenXPlugin> { | ||
private val log = logger() | ||
|
||
override val key: AttributeKey<TokenXPlugin> = AttributeKey("TokenXPlugin") | ||
val TokenXPlugin = createClientPlugin("TokenXPlugin", ::TokenXPluginConfig) { | ||
val log = logger() | ||
|
||
override fun prepare(block: Config.() -> Unit): TokenXPlugin { | ||
val config = Config().apply(block) | ||
return TokenXPlugin( | ||
audience = requireNotNull(config.audience) { | ||
"TokenXPlugin missing audience. Please configure audience." | ||
}, | ||
tokenXClient = config.tokenXClient ?: TokenXClientImpl() | ||
) | ||
} | ||
|
||
override fun install(plugin: TokenXPlugin, scope: HttpClient) { | ||
scope.requestPipeline.intercept(HttpRequestPipeline.Phases.State) { | ||
val subjectToken = context.headers[HttpHeaders.Authorization]?.removePrefix(BEARER_PREFIX) | ||
val tokenXClient = requireNotNull(pluginConfig.tokenXClient) { | ||
"TokenXPlugin: property 'tokenXClient' must be set in configuration when installing plugin" | ||
} | ||
val audience = requireNotNull(pluginConfig.audience) { | ||
"TokenXPlugin: property 'audience' must be set in configuration when installing plugin" | ||
} | ||
|
||
if (subjectToken == null) { | ||
log.warn("subjectToken not present. skipping exchange for audience {}", plugin.audience) | ||
} else { | ||
try { | ||
val accessToken = plugin.tokenXClient.exchange(subjectToken, plugin.audience) | ||
context.bearerAuth(accessToken) | ||
} catch (e: RuntimeException) { | ||
log.warn("failed to set bearer auth header due to exception in exchange ${e.message}", e) | ||
} | ||
} | ||
onRequest { request, _ -> | ||
val subjectToken = request.headers[HttpHeaders.Authorization]?.removePrefix(BEARER_PREFIX) | ||
|
||
proceed() | ||
if (subjectToken == null) { | ||
log.warn("subjectToken not present. skipping exchange for audience {}", audience) | ||
} else { | ||
try { | ||
request.bearerAuth(tokenXClient.exchange(subjectToken, audience)) | ||
} catch (e: RuntimeException) { | ||
log.warn("failed to set bearer auth header due to exception in exchange ${e.message}", e) | ||
} | ||
|
||
} | ||
} | ||
} |
76 changes: 76 additions & 0 deletions
76
...kotlin/no/nav/arbeidsgiver/notifikasjon/infrastruktur/altinn/AltinnTilgangerClientTest.kt
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,76 @@ | ||
package no.nav.arbeidsgiver.notifikasjon.infrastruktur.altinn | ||
|
||
import io.kotest.core.spec.style.DescribeSpec | ||
import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder | ||
import io.kotest.matchers.shouldBe | ||
import io.ktor.client.engine.mock.* | ||
import io.ktor.http.* | ||
import io.ktor.utils.io.* | ||
import no.nav.arbeidsgiver.notifikasjon.bruker.BrukerModel | ||
import no.nav.arbeidsgiver.notifikasjon.infrastruktur.tokenx.TokenXClientStub | ||
|
||
class AltinnTilgangerClientTest : DescribeSpec({ | ||
describe("AltinnTilgangerClient") { | ||
|
||
val client = AltinnTilgangerClient( | ||
tokenXClient = TokenXClientStub(), | ||
engine = MockEngine { _ -> | ||
respond( | ||
content = ByteReadChannel(altinnTilgangerResponse), | ||
status = HttpStatusCode.OK, | ||
headers = headersOf(HttpHeaders.ContentType, "application/json") | ||
) | ||
} | ||
) | ||
|
||
it("returns all tilganger") { | ||
client.hentTilganger().also { | ||
it.harFeil shouldBe true | ||
it.tjenestetilganger shouldContainExactlyInAnyOrder listOf( | ||
BrukerModel.Tilgang.Altinn("910825496", "test-fager", ""), | ||
BrukerModel.Tilgang.Altinn("910825496", "4936", "1"), | ||
) | ||
} | ||
} | ||
} | ||
|
||
}) | ||
|
||
private val altinnTilgangerResponse = """ | ||
{ | ||
"isError": true, | ||
"hierarki": [ | ||
{ | ||
"orgNr": "810825472", | ||
"altinn3Tilganger": [], | ||
"altinn2Tilganger": [], | ||
"underenheter": [ | ||
{ | ||
"orgNr": "910825496", | ||
"altinn3Tilganger": [ | ||
"test-fager" | ||
], | ||
"altinn2Tilganger": [ | ||
"4936:1" | ||
], | ||
"underenheter": [] | ||
} | ||
] | ||
} | ||
], | ||
"orgNrTilTilganger": { | ||
"910825496": [ | ||
"test-fager", | ||
"4936:1" | ||
] | ||
}, | ||
"tilgangTilOrgNr": { | ||
"test-fager": [ | ||
"910825496" | ||
], | ||
"4936:1": [ | ||
"910825496" | ||
] | ||
} | ||
} | ||
""".trimIndent() |