Skip to content

Commit

Permalink
slår opp med POST mot spleis med fnr i body
Browse files Browse the repository at this point in the history
spanner holder styr på maskering i route-grensesnittet, og ikke nede i Personer-interfacet.
  • Loading branch information
davidsteinsland committed Nov 8, 2024
1 parent cdedd59 commit 399b72f
Show file tree
Hide file tree
Showing 7 changed files with 158 additions and 69 deletions.
2 changes: 2 additions & 0 deletions backend/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ repositories {
dependencies {
implementation("com.github.navikt.tbd-libs:azure-token-client-default:$tbdLibsVersion")
implementation("com.github.navikt.tbd-libs:spurtedu-client:$tbdLibsVersion")
implementation("com.github.navikt.tbd-libs:speed-client:$tbdLibsVersion")

implementation("io.ktor:ktor-server-core:$ktorVersion")
implementation("io.ktor:ktor-server-call-id:$ktorVersion")
Expand Down Expand Up @@ -62,6 +63,7 @@ dependencies {
testImplementation("org.junit.jupiter:junit-jupiter:$junitJupiterVersion")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")

testImplementation("io.mockk:mockk:1.13.13")
testImplementation("no.nav.security:mock-oauth2-server:2.1.1")
testImplementation("io.ktor:ktor-server-test-host:$ktorVersion")
}
Expand Down
17 changes: 16 additions & 1 deletion backend/src/main/kotlin/no/nav/spanner/App.kt
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
package no.nav.spanner


import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.github.navikt.tbd_libs.azure.createAzureTokenClientFromEnvironment
import com.github.navikt.tbd_libs.speed.SpeedClient
import com.github.navikt.tbd_libs.spurtedu.SpurteDuClient
import com.natpryce.konfig.ConfigurationProperties
import com.natpryce.konfig.EnvironmentVariables
import com.natpryce.konfig.overriding
import io.ktor.server.cio.*
import io.ktor.server.engine.*
import org.slf4j.LoggerFactory
import java.io.File
import java.net.http.HttpClient


private val logg = Log.logger("Main")
Expand All @@ -25,14 +30,24 @@ fun main() {

val adConfig = AzureADConfig.fromEnv(configProps)
val tokenProvider = createAzureTokenClientFromEnvironment()

val speedClient = SpeedClient(
httpClient = HttpClient.newHttpClient(),
objectMapper = jacksonObjectMapper().registerModule(JavaTimeModule()),
tokenProvider = tokenProvider
)
val spurteDuClient = SpurteDuClient(
objectMapper = objectMapper,
tokenProvider = tokenProvider
)
val spleis = Spleis.from(spannerConfig, tokenProvider)

embeddedServer(CIO, environment = applicationEngineEnvironment {
log = LoggerFactory.getLogger("Spanner")
developmentMode = spannerConfig.development

module {
spanner(spleis, adConfig, spannerConfig.development)
spanner(spleis, speedClient, spurteDuClient, påkrevdSpurteduTilgang = System.getenv("SPURTE_DU_TILGANG"), adConfig, spannerConfig.development)
}

connector {
Expand Down
45 changes: 12 additions & 33 deletions backend/src/main/kotlin/no/nav/spanner/Personer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import com.fasterxml.jackson.databind.util.RawValue
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
import com.github.navikt.tbd_libs.azure.AzureTokenProvider
import com.github.navikt.tbd_libs.result_object.getOrThrow
import com.github.navikt.tbd_libs.spurtedu.SkjulRequest
import com.github.navikt.tbd_libs.spurtedu.SpurteDuClient
import io.ktor.client.*
import io.ktor.client.engine.cio.*
import io.ktor.client.plugins.*
Expand Down Expand Up @@ -35,8 +33,7 @@ import java.util.*
val logger = LoggerFactory.getLogger(Spleis::class.java)

interface Personer {
suspend fun person(call: ApplicationCall, maskertId: String)
suspend fun maskerPerson(call: ApplicationCall, id: String, idType: IdType)
suspend fun person(call: ApplicationCall, fnr: String)
suspend fun speilperson(call: ApplicationCall, fnr: String)
suspend fun hendelse(call: ApplicationCall, meldingsreferanse: String)
}
Expand All @@ -46,9 +43,7 @@ class Spleis(
private val baseUrl: String = "http://spleis-api.tbd.svc.cluster.local",
spleisScope: String,
private val sparsomBaseUrl: String = "http://sparsom-api.tbd.svc.cluster.local",
sparsomScope: String,
private val spurteDuClient: SpurteDuClient,
private valkrevdSpurteduTilgang: String = System.getenv("SPURTE_DU_TILGANG")
sparsomScope: String
) : Personer {
private val spleis = ClientIdWithName(spleisScope, "spleis")
private val sparsom = ClientIdWithName(sparsomScope, "sparsom")
Expand All @@ -69,11 +64,7 @@ class Spleis(
baseUrl = spannerConfig.spleisUrl,
spleisScope = spannerConfig.spleisScope,
sparsomBaseUrl = spannerConfig.sparsomUrl,
sparsomScope = spannerConfig.sparsomScope,
spurteDuClient = SpurteDuClient(
objectMapper = objectMapper,
tokenProvider = azureAD
)
sparsomScope = spannerConfig.sparsomScope
)

private val ApplicationCall.bearerToken: String? get() {
Expand All @@ -83,42 +74,30 @@ class Spleis(
}
}

override suspend fun maskerPerson(call: ApplicationCall, id: String, idType: IdType) {
val payload = SkjulRequest.SkjulTekstRequest(
tekst = objectMapper.writeValueAsString(mapOf(
"ident" to id,
"identtype" to when (idType) {
IdType.FNR, IdType.AKTORID -> idType.name
else -> throw BadRequestException("støtter kun fnr og aktorid")
}
)),
påkrevdTilgang = påkrevdSpurteduTilgang
)
val maskertId = spurteDuClient.skjul(payload)
call.respondText(""" { "id": "${maskertId.id}" } """, Json, OK)
}

override suspend fun person(call: ApplicationCall, maskertId: String) {
override suspend fun person(call: ApplicationCall, fnr: String) {
val accessToken = call.bearerToken ?: return call.respond(Unauthorized)
val url = URLBuilder(baseUrl).apply {
path("api", "person-json", maskertId)
path("api", "person-json")
}.build()

val log = Log.logger(this::class.java)

val response = coroutineScope {
val aktivitetslogg: Deferred<String?> = async(Dispatchers.IO) {
aktivitetslogg(accessToken, maskertId, call.callId ?: UUID.randomUUID().toString())
aktivitetslogg(accessToken, fnr, call.callId ?: UUID.randomUUID().toString())
}

val oboToken = spleis.token(azureAD, accessToken)

val response =
try {
httpClient.get(url) {
httpClient.post(url) {
header("Authorization", "Bearer $oboToken")
header(IdType.MASKERT_ID.header, maskertId)
accept(Json)
contentType(Json)
setBody(mapOf(
"fødselsnummer" to fnr
))
}
} catch (e: ClientRequestException) {
if (e.response.status == HttpStatusCode.NotFound) {
Expand Down Expand Up @@ -191,7 +170,7 @@ class Spleis(
.response(response)
.info("Response from sparsom")
response.bodyAsText()
} catch (e : ClientRequestException) {
} catch (_ : ClientRequestException) {
null
}
}
Expand Down
70 changes: 64 additions & 6 deletions backend/src/main/kotlin/no/nav/spanner/Spanner.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
package no.nav.spanner

import com.fasterxml.jackson.core.JsonParseException
import com.github.navikt.tbd_libs.result_object.getOrThrow
import com.github.navikt.tbd_libs.speed.SpeedClient
import com.github.navikt.tbd_libs.spurtedu.SkjulRequest
import com.github.navikt.tbd_libs.spurtedu.SpurteDuClient
import io.ktor.http.*
import io.ktor.http.ContentType.Application.Json
import io.ktor.http.HttpStatusCode.Companion.OK
import io.ktor.http.auth.HttpAuthHeader
import io.ktor.serialization.jackson.*
import io.ktor.server.application.*
import io.ktor.server.auth.*
Expand Down Expand Up @@ -28,7 +36,14 @@ enum class IdType(val header: String) {

private val logg = Log.logger("Spanner")

fun Application.spanner(spleis: Personer, config: AzureADConfig, development: Boolean) {
fun Application.spanner(
spleis: Personer,
speedClient: SpeedClient,
spurteDuClient: SpurteDuClient,
krevdSpurteduTilgang: String,
config: AzureADConfig,
development: Boolean,
) {

install(CallId) {
generate {
Expand Down Expand Up @@ -75,7 +90,7 @@ fun Application.spanner(spleis: Personer, config: AzureADConfig, development: Bo
}
}

install(ContentNegotiation) { register(ContentType.Application.Json, JacksonConverter(objectMapper)) }
install(ContentNegotiation) { register(Json, JacksonConverter(objectMapper)) }

install(Authentication) {
config.konfigurerJwtAuth(this)
Expand All @@ -90,14 +105,36 @@ fun Application.spanner(spleis: Personer, config: AzureADConfig, development: Bo
val principal = call.principal<JWTPrincipal>() ?: return@get call.respond(HttpStatusCode.Unauthorized)
val navn = principal["name"]
val ident = principal["NAVident"]
call.respondText("""{ "navn": "$navn", "ident": "$ident" } """, ContentType.Application.Json, HttpStatusCode.OK)
call.respondText("""{ "navn": "$navn", "ident": "$ident" } """, Json, OK)
}
get("/api/person/") {
audit()
val maskertId = call.request.headers[IdType.MASKERT_ID.header]
?: return@get call.respond(HttpStatusCode.BadRequest, FeilRespons("bad_request", "${IdType.AKTORID.header} or ${IdType.FNR.header} must be set"))
?: return@get call.respond(HttpStatusCode.BadRequest, FeilRespons("bad_request", "header ${IdType.MASKERT_ID.header} må være satt"))
logg.call(this.call).info()
spleis.person(call, maskertId)

val id = try {
UUID.fromString(maskertId)
} catch (_: Exception) {
return@get call.respond(HttpStatusCode.BadRequest, FeilRespons("bad_request", "maskert id må være uuid"))
}

val tekstinnhold = spurteDuClient.vis(id, call.bearerToken).text
val fødselsnummer = try {
val node = objectMapper.readTree(tekstinnhold)
val ident = node.path("ident").asText()
val identype = node.path("identtype").asText()
when (identype.lowercase()) {
"fnr" -> ident
else -> null
}
} catch (_: JsonParseException) {
null
}

if (fødselsnummer == null) return@get call.respond(HttpStatusCode.BadRequest, FeilRespons("bad_request", "maskertId pekte ikke på et fødselsnummer"))

spleis.person(call, fødselsnummer)
}
post("/api/uuid/") {
audit()
Expand All @@ -109,7 +146,22 @@ fun Application.spanner(spleis: Personer, config: AzureADConfig, development: Bo
.info()
if (idValue.isNullOrBlank())
return@post call.respond(HttpStatusCode.BadRequest, FeilRespons("bad_request", "${IdType.AKTORID.header} or ${IdType.FNR.header} must be set"))
spleis.maskerPerson(call, idValue, idType)

val fnr = when (idType) {
IdType.FNR -> idValue
IdType.AKTORID -> speedClient.hentFødselsnummerOgAktørId(idValue, call.callId ?: UUID.randomUUID().toString()).getOrThrow().fødselsnummer
IdType.MASKERT_ID -> return@post call.respond(HttpStatusCode.BadRequest, FeilRespons("bad_request", "${IdType.AKTORID.header} or ${IdType.FNR.header} must be set"))
}

val payload = SkjulRequest.SkjulTekstRequest(
tekst = objectMapper.writeValueAsString(mapOf(
"ident" to fnr,
"identtype" to IdType.FNR.name
)),
påkrevdTilgang = påkrevdSpurteduTilgang
)
val maskertId = spurteDuClient.skjul(payload)
call.respondText(""" { "id": "${maskertId.id}" } """, Json, OK)
}
/*
har ikke funksjonalitet fra frontend ennå, men kan kalles manuelt fra devtools feks:
Expand Down Expand Up @@ -161,3 +213,9 @@ private fun ApplicationCall.personId() =
} ?: request.headers[IdType.FNR.header]?.let {
Pair(IdType.FNR, it)
} ?: Pair(IdType.AKTORID, request.header(IdType.AKTORID.header))

private val ApplicationCall.bearerToken: String? get() {
val httpAuthHeader = request.parseAuthorizationHeader() ?: return null
if (httpAuthHeader !is HttpAuthHeader.Single) return null
return httpAuthHeader.blob
}
59 changes: 55 additions & 4 deletions backend/src/test/kotlin/E2ETest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,16 @@ package no.nav.spanner

import com.auth0.jwk.JwkProviderBuilder
import com.fasterxml.jackson.module.kotlin.readValue
import com.github.navikt.tbd_libs.speed.SpeedClient
import com.github.navikt.tbd_libs.spurtedu.SkjulRequest
import com.github.navikt.tbd_libs.spurtedu.SkjulResponse
import com.github.navikt.tbd_libs.spurtedu.SpurteDuClient
import com.github.navikt.tbd_libs.spurtedu.VisTekstResponse
import io.ktor.http.*
import io.ktor.server.plugins.NotFoundException
import io.ktor.server.testing.*
import io.mockk.every
import io.mockk.mockk
import no.nav.security.mock.oauth2.MockOAuth2Server
import no.nav.security.mock.oauth2.token.DefaultOAuth2TokenCallback
import no.nav.spanner.no.nav.spanner.LokaleKjenninger
Expand All @@ -12,6 +20,7 @@ import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.Test
import java.util.UUID

class E2ETest {

Expand All @@ -20,6 +29,48 @@ class E2ETest {
issuer = "default",
clientId = "whatever"
)
private val speedClient = mockk<SpeedClient>()
private val påkrevdSpurteduTilgang = "skikkelig_tøysete_claim"
private val spurteDuClient = mockk<SpurteDuClient> {
val skjulRequests = mutableListOf<SkjulRequest>()
every { skjul(capture(skjulRequests)) } answers {
val forrigeRequest = skjulRequests.last()
check(forrigeRequest.påkrevdTilgang == påkrevdSpurteduTilgang) {
"påkrevd tilgang er ikke $påkrevdSpurteduTilgang"
}
val maskertId = when (forrigeRequest) {
is SkjulRequest.SkjulTekstRequest -> {
val skjultVerdi = objectMapper.readTree(forrigeRequest.tekst).path("ident").asText()
when (skjultVerdi) {
"42",
"12020052345" -> "113eb3df-102d-4a07-9270-2caa648c62f4"
"2392363031327" -> "48bfef57-3080-4f19-98eb-5cc72d9d16c5"
else -> throw NotFoundException("no person with identifier: $skjultVerdi")
}
}
is SkjulRequest.SkjulUrlRequest -> error("Støtter ikke dette")
}
SkjulResponse(
id = UUID.fromString(maskertId),
url = "http://foo/",
path = "/foo"
)
}

val visRequests = mutableListOf<UUID>()
every { vis(capture(visRequests), any(), any()) } answers {
val maskertId = visRequests.last()
val ident = when (maskertId.toString()) {
"113eb3df-102d-4a07-9270-2caa648c62f4" -> "12020052345"
"48bfef57-3080-4f19-98eb-5cc72d9d16c5" -> "2392363031327"
else -> error("Kjenner ikke til $maskertId")
}
VisTekstResponse(text = """{
"ident": "$ident",
"identtype": "FNR"
}""")
}
}

private val lokaleKjenninger = LokaleKjenninger

Expand All @@ -31,7 +82,7 @@ class E2ETest {
)
)
withTestApplication({
spanner(lokaleKjenninger, azureADConfig, true)
spanner(lokaleKjenninger, speedClient, spurteDuClient, påkrevdSpurteduTilgang, azureADConfig, true)
}) {
cookiesSession {
val uuid = objectMapper.readTree(handleRequest(HttpMethod.Post, "/api/uuid/") {
Expand Down Expand Up @@ -59,7 +110,7 @@ class E2ETest {
)
)
withTestApplication({
spanner(lokaleKjenninger, azureADConfig, true)
spanner(lokaleKjenninger, speedClient, spurteDuClient, påkrevdSpurteduTilgang, azureADConfig, true)
}) {
cookiesSession {
handleRequest(HttpMethod.Get, "/api/hendelse/42").apply {
Expand All @@ -82,7 +133,7 @@ class E2ETest {
)
)
withTestApplication({
spanner(lokaleKjenninger, azureADConfig, true)
spanner(lokaleKjenninger, speedClient, spurteDuClient, påkrevdSpurteduTilgang, azureADConfig, true)
}) {
cookiesSession {
val uuid = objectMapper.readTree(handleRequest(HttpMethod.Post, "/api/uuid/") {
Expand All @@ -104,7 +155,7 @@ class E2ETest {
@Test
fun `valid json error message response`() {
withTestApplication({
spanner(lokaleKjenninger, azureADConfig, true)
spanner(lokaleKjenninger, speedClient, spurteDuClient, påkrevdSpurteduTilgang, azureADConfig, true)
}) {
cookiesSession {
handleRequest(HttpMethod.Get, "/api/person/") { /* ingen ident i header */ }
Expand Down
Loading

0 comments on commit 399b72f

Please sign in to comment.