Skip to content

Commit 789575f

Browse files
committed
Lagt til noe tilgangsstyring for token, nytt GET-endepunkt for bekreftelse-api
1 parent 1a83c5e commit 789575f

26 files changed

+390
-248
lines changed

apps/bekreftelse-api/src/main/kotlin/no/nav/paw/bekreftelse/api/Application.kt

+1-3
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,7 @@ fun Application.module(
5353
configureLogging()
5454
configureSerialization()
5555
configureTracing()
56-
if (!applicationConfig.brukMock) { // TODO Bruker mock for utvikling
57-
configureKafka(applicationConfig, listOf(dependencies.bekreftelseKafkaStreams))
58-
}
56+
configureKafka(applicationConfig, listOf(dependencies.bekreftelseKafkaStreams))
5957

6058
routing {
6159
healthRoutes(dependencies.healthIndicatorRepository)

apps/bekreftelse-api/src/main/kotlin/no/nav/paw/bekreftelse/api/Dependencies.kt

+7-17
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ import no.nav.paw.bekreftelse.api.kafka.buildBekreftelseTopology
1111
import no.nav.paw.bekreftelse.api.plugins.buildKafkaStreams
1212
import no.nav.paw.bekreftelse.api.services.AutorisasjonService
1313
import no.nav.paw.bekreftelse.api.services.BekreftelseService
14-
import no.nav.paw.bekreftelse.api.services.BekreftelseServiceImpl
15-
import no.nav.paw.bekreftelse.api.services.BekreftelseServiceMock
1614
import no.nav.paw.health.repository.HealthIndicatorRepository
1715
import no.nav.paw.kafkakeygenerator.auth.azureAdM2MTokenClient
1816
import no.nav.paw.kafkakeygenerator.client.KafkaKeysClient
@@ -50,22 +48,14 @@ fun createDependencies(applicationConfig: ApplicationConfig): Dependencies {
5048
val bekreftelseTopology = buildBekreftelseTopology(applicationConfig, prometheusMeterRegistry)
5149
val bekreftelseKafkaStreams = buildKafkaStreams(applicationConfig, healthIndicatorRepository, bekreftelseTopology)
5250

53-
// TODO Bruker mock for utvikling
54-
val bekreftelseService: BekreftelseService = if (applicationConfig.brukMock) {
55-
healthIndicatorRepository.getLivenessIndicators().forEach { it.setHealthy() }
56-
healthIndicatorRepository.getReadinessIndicators().forEach { it.setHealthy() }
51+
val bekreftelseProducer = BekreftelseProducer(applicationConfig)
5752

58-
BekreftelseServiceMock()
59-
} else {
60-
val bekreftelseProducer = BekreftelseProducer(applicationConfig)
61-
62-
BekreftelseServiceImpl(
63-
applicationConfig,
64-
httpClient,
65-
bekreftelseKafkaStreams,
66-
bekreftelseProducer
67-
)
68-
}
53+
val bekreftelseService = BekreftelseService(
54+
applicationConfig,
55+
httpClient,
56+
bekreftelseKafkaStreams,
57+
bekreftelseProducer
58+
)
6959

7060
return Dependencies(
7161
kafkaKeysClient,

apps/bekreftelse-api/src/main/kotlin/no/nav/paw/bekreftelse/api/authz/ArbeidsoekerIdIkkeFunnetException.kt

-8
This file was deleted.

apps/bekreftelse-api/src/main/kotlin/no/nav/paw/bekreftelse/api/authz/BearerTokenManglerException.kt

-8
This file was deleted.

apps/bekreftelse-api/src/main/kotlin/no/nav/paw/bekreftelse/api/authz/Claim.kt

+12-5
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,20 @@ package no.nav.paw.bekreftelse.api.authz
33
import no.nav.paw.bekreftelse.api.model.Identitetsnummer
44
import java.util.*
55

6+
sealed class Issuer(
7+
val name: String
8+
)
9+
10+
data object TokenX : Issuer("tokenx")
11+
data object Azure : Issuer("azure")
12+
613
sealed class Claim<A : Any>(
7-
val issuer: String,
14+
val issuer: Issuer,
815
val claimName: String,
916
val fromString: (String) -> A
1017
)
1118

12-
data object AzureName : Claim<String>("azure", "name", { it })
13-
data object AzureNavIdent : Claim<String>("azure", "NAVident", { it })
14-
data object AzureOID : Claim<UUID>("azure", "oid", UUID::fromString)
15-
data object TokenXPID : Claim<Identitetsnummer>("tokenx", "pid", ::Identitetsnummer)
19+
data object AzureName : Claim<String>(Azure, "name", { it })
20+
data object AzureNavIdent : Claim<String>(Azure, "NAVident", { it })
21+
data object AzureOID : Claim<UUID>(Azure, "oid", UUID::fromString)
22+
data object TokenXPID : Claim<Identitetsnummer>(TokenX, "pid", ::Identitetsnummer)
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,51 @@
11
package no.nav.paw.bekreftelse.api.authz
22

3+
import io.ktor.http.HttpHeaders
34
import io.ktor.server.application.ApplicationCall
45
import io.ktor.server.application.call
56
import io.ktor.server.auth.principal
7+
import io.ktor.server.plugins.BadRequestException
68
import io.ktor.server.request.path
79
import io.ktor.util.pipeline.PipelineContext
810
import io.opentelemetry.instrumentation.annotations.WithSpan
9-
import no.nav.paw.bekreftelse.api.model.Identitetsnummer
11+
import no.nav.paw.bekreftelse.api.exception.BearerTokenManglerException
12+
import no.nav.paw.bekreftelse.api.exception.UfullstendigBearerTokenException
13+
import no.nav.paw.bekreftelse.api.model.BrukerType
14+
import no.nav.paw.bekreftelse.api.model.InnloggetBruker
15+
import no.nav.paw.bekreftelse.api.model.Sluttbruker
1016
import no.nav.paw.bekreftelse.api.services.AutorisasjonService
1117
import no.nav.paw.kafkakeygenerator.client.KafkaKeysClient
1218
import no.nav.poao_tilgang.client.TilgangType
1319
import no.nav.security.token.support.v2.TokenValidationContextPrincipal
1420

1521
data class RequestScope(
16-
val identitetsnummer: Identitetsnummer,
17-
val arbeidssoekerId: Long,
22+
val sluttbruker: Sluttbruker,
23+
val innloggetBruker: InnloggetBruker,
1824
val claims: ResolvedClaims,
1925
val path: String,
20-
val bearerToken: String,
2126
val callId: String?,
2227
val traceparent: String?,
2328
val navConsumerId: String?,
29+
val useMockData: Boolean
2430
)
2531

32+
@Suppress("ConstPropertyName")
33+
object NavHttpHeaders {
34+
const val TraceParent = "traceparent"
35+
const val NavCallId = "Nav-Call-Id"
36+
const val NavConsumerId = "Nav-Consumer-Id"
37+
}
38+
2639
@WithSpan
2740
suspend fun PipelineContext<Unit, ApplicationCall>.requestScope(
28-
identitetsnummer: String,
41+
identitetsnummer: String?,
2942
kafkaKeyClient: KafkaKeysClient,
3043
autorisasjonService: AutorisasjonService, // TODO Legg til autorisasjon
3144
tilgangType: TilgangType
3245
): RequestScope {
46+
val bearerToken = call.request.headers[HttpHeaders.Authorization]
47+
?: throw BearerTokenManglerException("Request mangler Bearer Token")
48+
3349
val tokenValidationContext = call.principal<TokenValidationContextPrincipal>()
3450

3551
val resolvedClaims = tokenValidationContext
@@ -41,17 +57,51 @@ suspend fun PipelineContext<Unit, ApplicationCall>.requestScope(
4157
TokenXPID
4258
) ?: ResolvedClaims()
4359

44-
val kafkaKeysResponse = kafkaKeyClient.getIdAndKey(identitetsnummer)
45-
val headers = call.request.headers
60+
if (resolvedClaims.isEmpty()) {
61+
throw UfullstendigBearerTokenException("Bearer Token mangler påkrevd innhold")
62+
}
4663

47-
return RequestScope(
48-
identitetsnummer = Identitetsnummer(identitetsnummer),
64+
// TODO Håndtere at fnr kommer i body
65+
val resolvedIdentitetsnummer = if (resolvedClaims.isTokenX()) {
66+
resolvedClaims[TokenXPID]?.verdi
67+
?: throw UfullstendigBearerTokenException("Bearer Token mangler påkrevd innhold")
68+
} else if (resolvedClaims.isAzure()) {
69+
identitetsnummer ?: throw BadRequestException("Request mangler identitetsnummer")
70+
} else {
71+
throw UfullstendigBearerTokenException("Bearer Token er utstedt av ukjent issuer")
72+
}
73+
74+
val kafkaKeysResponse = kafkaKeyClient.getIdAndKey(resolvedIdentitetsnummer)
75+
val sluttbruker = Sluttbruker(
76+
identitetsnummer = resolvedIdentitetsnummer,
4977
arbeidssoekerId = kafkaKeysResponse.id,
78+
kafkaKey = kafkaKeysResponse.key
79+
)
80+
81+
val innloggetBruker = if (resolvedClaims.isTokenX()) {
82+
InnloggetBruker(
83+
type = BrukerType.SLUTTBRUKER,
84+
ident = resolvedIdentitetsnummer,
85+
bearerToken = bearerToken
86+
)
87+
} else {
88+
val ident = resolvedClaims[AzureNavIdent]
89+
?: throw UfullstendigBearerTokenException("Bearer Token mangler påkrevd innhold")
90+
InnloggetBruker(
91+
type = BrukerType.VEILEDER,
92+
ident = ident,
93+
bearerToken = bearerToken
94+
)
95+
}
96+
97+
return RequestScope(
98+
sluttbruker = sluttbruker,
99+
innloggetBruker = innloggetBruker,
50100
claims = resolvedClaims,
51101
path = call.request.path(),
52-
bearerToken = headers["Authorization"] ?: throw BearerTokenManglerException("Request mangler Bearer Token"),
53-
callId = headers["Nav-Call-Id"],
54-
traceparent = headers["traceparent"],
55-
navConsumerId = headers["Nav-Consumer-Id"]
102+
callId = call.request.headers[NavHttpHeaders.NavCallId],
103+
traceparent = call.request.headers[NavHttpHeaders.TraceParent],
104+
navConsumerId = call.request.headers[NavHttpHeaders.NavConsumerId],
105+
useMockData = call.request.queryParameters["useMockData"].toBoolean()
56106
)
57107
}

apps/bekreftelse-api/src/main/kotlin/no/nav/paw/bekreftelse/api/authz/ResolvedClaims.kt

+9-2
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,20 @@ class ResolvedClaims private constructor(
1414

1515
@Suppress("UNCHECKED_CAST")
1616
operator fun <T : Any> get(claim: Claim<T>): T? = map[claim] as T?
17-
fun isResolved(claim: Claim<*>): Boolean = map.containsKey(claim)
1817

1918
fun <T : Any> add(claim: Claim<T>, rawValue: String): ResolvedClaims {
2019
val parsed = claim.fromString(rawValue)
2120
val pair: Pair<Claim<T>, Any> = claim to parsed
2221
return ResolvedClaims(map + pair)
2322
}
23+
24+
fun isTokenX() = map.isNotEmpty() && map.keys.filter { it.issuer == TokenX }.size == map.size
25+
26+
fun isAzure() = map.isNotEmpty() && map.keys.filter { it.issuer == Azure }.size == map.size
27+
28+
fun isResolved(claim: Claim<*>): Boolean = map.containsKey(claim)
29+
30+
fun isEmpty() = map.isEmpty()
2431
}
2532

2633
fun TokenValidationContext?.resolveClaims(vararg claims: Claim<*>): ResolvedClaims =
@@ -31,7 +38,7 @@ fun TokenValidationContext?.resolveClaims(vararg claims: Claim<*>): ResolvedClai
3138
}
3239

3340
fun TokenValidationContext?.resolve(claim: Claim<*>): String? =
34-
this?.getClaimOrNull(claim.issuer)
41+
this?.getClaimOrNull(claim.issuer.name)
3542
?.getStringClaim(claim.claimName)
3643

3744
fun TokenValidationContext.getClaimOrNull(issuer: String) =

apps/bekreftelse-api/src/main/kotlin/no/nav/paw/bekreftelse/api/authz/VeilerHarIkkeTilgangException.kt

-8
This file was deleted.

apps/bekreftelse-api/src/main/kotlin/no/nav/paw/bekreftelse/api/config/ApplicationConfig.kt

+1-3
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,7 @@ data class ApplicationConfig(
2222
val naisEnv: NaisEnv = currentNaisEnv,
2323
val appId: String = currentAppId ?: "UNSPECIFIED",
2424
val appName: String = currentAppName ?: "UNSPECIFIED",
25-
val hostname: String = InetAddress.getLocalHost().hostName,
26-
27-
val brukMock: Boolean = true
25+
val hostname: String = InetAddress.getLocalHost().hostName
2826
)
2927

3028
data class KafkaTopologyConfig(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package no.nav.paw.bekreftelse.api.exception
2+
3+
import no.nav.paw.error.exception.AuthorizationException
4+
5+
class ArbeidssoekerIdIkkeFunnetException(message: String) :
6+
AuthorizationException("PAW_ARBEIDSSOEKER_ID_IKKE_FUNNET", message, null)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package no.nav.paw.bekreftelse.api.exception
2+
3+
import no.nav.paw.error.exception.AuthenticationException
4+
5+
class BearerTokenManglerException(message: String) : AuthenticationException("PAW_BEARER_TOKEN_MANGLER", message, null)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package no.nav.paw.bekreftelse.api.exception
2+
3+
import no.nav.paw.error.exception.AuthorizationException
4+
5+
class UfullstendigBearerTokenException(message: String) :
6+
AuthorizationException("PAW_UFULLSTENDIG_BEARER_TOKEN", message, null)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package no.nav.paw.bekreftelse.api.exception
2+
3+
import no.nav.paw.error.exception.AuthorizationException
4+
5+
class VeilerHarIkkeTilgangException(message: String) :
6+
AuthorizationException("PAW_VEILEDER_HAR_IKKE_TILGANG", message, null) {
7+
}

apps/bekreftelse-api/src/main/kotlin/no/nav/paw/bekreftelse/api/kafka/BekreftelseHendelseProcessor.kt

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package no.nav.paw.bekreftelse.api.kafka
22

3+
import io.micrometer.core.instrument.MeterRegistry
34
import no.nav.paw.bekreftelse.api.model.InternState
45
import no.nav.paw.bekreftelse.internehendelser.BekreftelseHendelse
56
import no.nav.paw.bekreftelse.internehendelser.BekreftelseMeldingMottatt
@@ -13,16 +14,18 @@ import org.apache.kafka.streams.processor.api.Record
1314
import org.apache.kafka.streams.state.KeyValueStore
1415

1516
fun KStream<Long, BekreftelseHendelse>.oppdaterBekreftelseHendelseState(
16-
stateStoreName: String
17+
stateStoreName: String,
18+
meterRegistry: MeterRegistry
1719
): KStream<Long, BekreftelseHendelse> {
1820
val processor = {
19-
BekreftelseHendelseProcessor(stateStoreName)
21+
BekreftelseHendelseProcessor(stateStoreName, meterRegistry)
2022
}
2123
return process(processor, Named.`as`("bekreftelseHendelseProcessor"), stateStoreName)
2224
}
2325

2426
class BekreftelseHendelseProcessor(
2527
private val stateStoreName: String,
28+
meterRegistry: MeterRegistry
2629
) : Processor<Long, BekreftelseHendelse, Long, BekreftelseHendelse> {
2730
private var stateStore: KeyValueStore<Long, InternState>? = null
2831
private var context: ProcessorContext<Long, BekreftelseHendelse>? = null
@@ -33,9 +36,10 @@ class BekreftelseHendelseProcessor(
3336
stateStore = context?.getStateStore(stateStoreName)
3437
}
3538

39+
// TODO Legg til metrics
3640
override fun process(record: Record<Long, BekreftelseHendelse>?) {
3741
val value = record?.value() ?: return
38-
val hendelseStore = requireNotNull(stateStore) { "State store is not initialized" }
42+
val hendelseStore = requireNotNull(stateStore) { "Intern state store er ikke initiert" }
3943
when (value) {
4044
is BekreftelseTilgjengelig -> {
4145
hendelseStore.get(value.arbeidssoekerId)?.let {

apps/bekreftelse-api/src/main/kotlin/no/nav/paw/bekreftelse/api/kafka/BekreftelseProducer.kt

-19
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
package no.nav.paw.bekreftelse.api.kafka
22

33
import no.nav.paw.bekreftelse.api.config.ApplicationConfig
4-
import no.nav.paw.bekreftelse.api.model.BekreftelseRequest
54
import no.nav.paw.bekreftelse.api.utils.buildBekreftelseSerde
65
import no.nav.paw.bekreftelse.api.utils.logger
7-
import no.nav.paw.bekreftelse.internehendelser.BekreftelseTilgjengelig
86
import no.nav.paw.bekreftelse.melding.v1.Bekreftelse
97
import no.nav.paw.config.kafka.KafkaFactory
108
import no.nav.paw.config.kafka.sendDeferred
@@ -43,20 +41,3 @@ class BekreftelseProducer(
4341
producer.close()
4442
}
4543
}
46-
47-
fun createMelding(state: BekreftelseTilgjengelig, bekreftelse: BekreftelseRequest): Bekreftelse = TODO()
48-
//Melding.newBuilder()
49-
// .setId(ApplicationInfo.id)
50-
// .setNamespace("paw")
51-
// .setPeriodeId(state.periodeId)
52-
// .setSvar(Svar(
53-
// Metadata(
54-
// Instant.now(),
55-
// Bruker
56-
// )
57-
// ))
58-
// .setGjelderFra(state.gjelderFra)
59-
// .setGjelderTil(state.gjelderTil)
60-
// .setVilFortsetteSomArbeidssoeker(bekreftelse.vilFortsetteSomArbeidssoeker)
61-
// .setHarJobbetIDennePerioden(bekreftelse.harJobbetIDennePerioden)
62-
// .build()

apps/bekreftelse-api/src/main/kotlin/no/nav/paw/bekreftelse/api/kafka/Topology.kt

+6-3
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ fun buildBekreftelseTopology(
1515
meterRegistry: MeterRegistry
1616
): Topology = StreamsBuilder().apply {
1717
addInternStateStore(applicationConfig)
18-
addBekreftelseKStream(applicationConfig)
18+
addBekreftelseKStream(applicationConfig, meterRegistry)
1919
}.build()
2020

2121
private fun StreamsBuilder.addInternStateStore(applicationConfig: ApplicationConfig) {
@@ -28,10 +28,13 @@ private fun StreamsBuilder.addInternStateStore(applicationConfig: ApplicationCon
2828
)
2929
}
3030

31-
private fun StreamsBuilder.addBekreftelseKStream(applicationConfig: ApplicationConfig) {
31+
private fun StreamsBuilder.addBekreftelseKStream(
32+
applicationConfig: ApplicationConfig,
33+
meterRegistry: MeterRegistry
34+
) {
3235
stream(
3336
applicationConfig.kafkaTopology.bekreftelseHendelsesloggTopic,
3437
Consumed.with(Serdes.Long(), BekreftelseHendelseSerde())
3538
)
36-
.oppdaterBekreftelseHendelseState(applicationConfig.kafkaTopology.internStateStoreName)
39+
.oppdaterBekreftelseHendelseState(applicationConfig.kafkaTopology.internStateStoreName, meterRegistry)
3740
}

0 commit comments

Comments
 (0)