diff --git a/build.gradle.kts b/build.gradle.kts index 8310a807..22cb3e17 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,7 +7,7 @@ val mockkVersion = "1.13.8" val wireMockVersion = "2.19.0" val filformatVersion = "1.2019.06.26-14.50-746e7610cb12" val micrometerRegistryVersion = "1.1.2" -val tokenValidationVersion = "2.1.7" +val tokenSupportVersion = "2.1.7" val jacksonVersion = "2.9.9" val springdocVersion = "1.6.15" val navFoedselsnummerVersion = "1.0-SNAPSHOT.6" @@ -15,7 +15,6 @@ val skattKontraktVersjon = "2.0_20230214104704_706e9c0" val fellesVersjon = "1.20230116145510_2afcc20" val kontrakterVersjon = "2.0_20230313140330_0086324" val coroutinesVersion = "1.6.4" -val okhttp3Version = "4.9.3" val mainClass = "no.nav.familie.ba.infotrygd.Main" @@ -77,12 +76,8 @@ dependencies { implementation("io.micrometer:micrometer-registry-prometheus") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") implementation("net.ttddyy:datasource-proxy:1.8.1") - implementation("no.nav.security:token-validation-spring:$tokenValidationVersion") - testImplementation("no.nav.security:token-validation-spring-test:$tokenValidationVersion") { - exclude(group = "com.squareup.okhttp3", module = "mockwebserver") - } - testImplementation("com.squareup.okhttp3:mockwebserver:$okhttp3Version") - testImplementation("com.squareup.okhttp3:okhttp:$okhttp3Version") + implementation("no.nav.security:token-validation-spring:$tokenSupportVersion") + testImplementation("no.nav.security:token-validation-test-support:2.0.5") implementation("javax.inject:javax.inject:1") implementation("org.jetbrains.kotlin:kotlin-reflect") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") diff --git a/nais/dev-fss.yml b/nais/dev-fss.yml index 58d2a6b3..fc309546 100644 --- a/nais/dev-fss.yml +++ b/nais/dev-fss.yml @@ -81,9 +81,6 @@ spec: groups: - id: c62e908a-cf20-4ad0-b7b3-3ff6ca4bf38b # teamfamilie-forvaltning - id: 928636f4-fd0d-4149-978e-a6fb68bb19de # 0000-GA-STDAPPS - tilgang til prosessering - - id: 93a26831-9866-4410-927b-74ff51a9107c # VEILEDER_ROLLE - - id: d21e00a4-969d-4b28-8782-dc818abfae65 # SAKSBEHANDLER_ROLLE - - id: 9449c153-5a1e-44a7-84c6-7cc7a8867233 # BESLUTTER_ROLLE tenant: trygdeetaten.no replyURLs: - "https://familie-ba-infotrygd.dev.intern.nav.no/swagger-ui/oauth2-redirect.html" @@ -103,6 +100,8 @@ spec: value: none - name: APP_AZURE_PROXY_URL value: https://webproxy-nais.nav.no:8088 + - name: APP_CLIENT_WHITELIST + value: azure/146ccc69-7cd0-4b8e-86a5-144534e53a00,azure/288f2ef5-23fa-4fc5-af6b-6001adaf9e50,azure/e50c7d59-7183-4978-8d8c-8af7f9a7e6a9,azure/1ded84b0-21b7-4042-9c84-820c939021a6,azure/dc2b8dca-9de9-42f7-9103-f52a9169428e,azure/a38ae9cf-f55a-480a-b830-cc68d53f8445,azure/b5b51c6e-aa53-4ab8-ae68-f7aed5ae4b1d - name: APP_DATASOURCE_USERNAME_PATH value: /var/run/secrets/oracle/creds/username - name: APP_DATASOURCE_PASSWORD_PATH diff --git a/nais/prod-fss.yml b/nais/prod-fss.yml index 5b77524d..6224f095 100644 --- a/nais/prod-fss.yml +++ b/nais/prod-fss.yml @@ -80,9 +80,6 @@ spec: groups: - id: 3d718ae5-f25e-47a4-b4b3-084a97604c1d # teamfamilie-forvaltning - id: 87190cf3-b278-457d-8ab7-1a5c55a9edd7 # Group_87190cf3-b278-457d-8ab7-1a5c55a9edd7 tilgang til prosessering - - id: 199c2b39-e535-4ae8-ac59-8ccbee7991ae # veileder - - id: 847e3d72-9dc1-41c3-80ff-f5d4acdd5d46 # saksbehandler - - id: 7a271f87-39fb-468b-a9ee-6cf3c070f548 # beslutter replyURLs: - "https://familie-ba-infotrygd.intern.nav.no/swagger-ui/oauth2-redirect.html" singlePageApplication: true @@ -101,6 +98,8 @@ spec: value: none - name: APP_AZURE_PROXY_URL value: https://webproxy-nais.nav.no:8088 + - name: APP_CLIENT_WHITELIST + value: azure/0412f3eb-b22a-4d3f-99aa-319321eb340f,azure/984d4731-b95d-4f87-8154-d8f153f0ebee,azure/a1e0a7ad-7838-41c4-bf64-84b689d569e9,azure/f1fe85a0-704b-42e9-bd1e-5793be962010,azure/bc8e9286-2696-416d-b658-522301569562,azure/e8228369-577e-474e-b426-a76c275fd2fa - name: APP_DATASOURCE_USERNAME_PATH value: /var/run/secrets/oracle/creds/username - name: APP_DATASOURCE_PASSWORD_PATH diff --git a/src/main/kotlin/no/nav/familie/ba/infotrygd/config/SecurityConfiguration.kt b/src/main/kotlin/no/nav/familie/ba/infotrygd/config/SecurityConfiguration.kt index 2db7cfe2..2952005d 100644 --- a/src/main/kotlin/no/nav/familie/ba/infotrygd/config/SecurityConfiguration.kt +++ b/src/main/kotlin/no/nav/familie/ba/infotrygd/config/SecurityConfiguration.kt @@ -1,8 +1,11 @@ package no.nav.familie.ba.infotrygd.config +import no.nav.familie.ba.infotrygd.Profiles import no.nav.security.token.support.spring.api.EnableJwtTokenValidation import org.springframework.context.annotation.Configuration +import org.springframework.context.annotation.Profile @EnableJwtTokenValidation(ignore = ["org.springframework", "springfox", "org.springdoc"]) +@Profile("!${Profiles.NOAUTH}") @Configuration class SecurityConfiguration diff --git a/src/main/kotlin/no/nav/familie/ba/infotrygd/config/SwaggerConfig.kt b/src/main/kotlin/no/nav/familie/ba/infotrygd/config/SwaggerConfig.kt index 37b87305..6eba5edb 100644 --- a/src/main/kotlin/no/nav/familie/ba/infotrygd/config/SwaggerConfig.kt +++ b/src/main/kotlin/no/nav/familie/ba/infotrygd/config/SwaggerConfig.kt @@ -13,14 +13,12 @@ import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration @Configuration -class SwaggerConfig( - @Value("\${AUTHORIZATION_URL}") - val authorizationUrl: String, - @Value("\${TOKEN_URL}") - val tokenUrl: String, - @Value("\${API_SCOPE}") - val apiScope: String -) { +class SwaggerConfig(@Value("\${AUTHORIZATION_URL}") + val authorizationUrl: String, + @Value("\${AZURE_OPENID_CONFIG_TOKEN_ENDPOINT}") + val tokenUrl: String, + @Value("\${API_SCOPE}") + val apiScope: String) { @Bean fun openApi(): OpenAPI { diff --git a/src/main/kotlin/no/nav/familie/ba/infotrygd/rest/controller/BarnetrygdController.kt b/src/main/kotlin/no/nav/familie/ba/infotrygd/rest/controller/BarnetrygdController.kt index d4c87ec2..79220b84 100644 --- a/src/main/kotlin/no/nav/familie/ba/infotrygd/rest/controller/BarnetrygdController.kt +++ b/src/main/kotlin/no/nav/familie/ba/infotrygd/rest/controller/BarnetrygdController.kt @@ -9,10 +9,10 @@ import no.nav.familie.ba.infotrygd.model.dl1.Hendelse import no.nav.familie.ba.infotrygd.rest.api.InfotrygdLøpendeBarnetrygdResponse import no.nav.familie.ba.infotrygd.rest.api.InfotrygdÅpenSakResponse import no.nav.familie.ba.infotrygd.service.BarnetrygdService -import no.nav.familie.ba.infotrygd.service.TilgangskontrollService +import no.nav.familie.ba.infotrygd.service.ClientValidator import no.nav.familie.kontrakter.ba.infotrygd.InfotrygdSøkRequest import no.nav.familie.kontrakter.ba.infotrygd.InfotrygdSøkResponse -import no.nav.security.token.support.core.api.ProtectedWithClaims +import no.nav.security.token.support.core.api.Protected import org.slf4j.LoggerFactory import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.GetMapping @@ -27,13 +27,13 @@ import no.nav.familie.kontrakter.ba.infotrygd.Sak as SakDto import no.nav.familie.kontrakter.ba.infotrygd.Stønad as StønadDto +@Protected @RestController -@ProtectedWithClaims(issuer = "azuread") @Timed(value = "infotrygd_historikk_barnetrygd_controller", percentiles = [0.5, 0.95]) @RequestMapping("/infotrygd/barnetrygd") class BarnetrygdController( private val barnetrygdService: BarnetrygdService, - private val tilgangskontrollService: TilgangskontrollService + private val clientValidator: ClientValidator ) { private val logger = LoggerFactory.getLogger(javaClass) @@ -41,7 +41,7 @@ class BarnetrygdController( @PostMapping(path = ["lopende-barnetrygd"], consumes = ["application/json"]) @ApiRequestBody(content = [Content(examples = [ExampleObject(value = INFOTRYGD_SØK_EKSEMPEL)])]) fun harLopendeBarnetrygd(@RequestBody request: InfotrygdSøkRequest): ResponseEntity { - tilgangskontrollService.sjekkTilgang() + clientValidator.authorizeClient() val harLøpendeBarnetrygd = hentStønaderPåBrukereOgBarn(request.brukere, request.barn, false).let { it.first.isNotEmpty() || it.second.isNotEmpty() @@ -53,7 +53,7 @@ class BarnetrygdController( @PostMapping(path = ["aapen-sak"], consumes = ["application/json"]) @ApiRequestBody(content = [Content(examples = [ExampleObject(value = INFOTRYGD_SØK_EKSEMPEL)])]) fun harÅpenSak(@RequestBody request: InfotrygdSøkRequest): ResponseEntity { - tilgangskontrollService.sjekkTilgang() + clientValidator.authorizeClient() return barnetrygdService.tellAntallÅpneSaker(request.brukere, request.barn).let { ResponseEntity.ok(InfotrygdÅpenSakResponse(it > 0)) @@ -67,7 +67,7 @@ class BarnetrygdController( @RequestBody request: InfotrygdSøkRequest, @RequestParam(required = false) historikk: Boolean? ): ResponseEntity> { - tilgangskontrollService.sjekkTilgang() + clientValidator.authorizeClient() return hentStønaderPåBrukereOgBarn(request.brukere, request.barn, historikk).let { ResponseEntity.ok(InfotrygdSøkResponse(bruker = it.first, barn = it.second)) @@ -78,7 +78,7 @@ class BarnetrygdController( @PostMapping(path = ["saker"], consumes = ["application/json"]) @ApiRequestBody(content = [Content(examples = [ExampleObject(value = INFOTRYGD_SØK_EKSEMPEL)])]) fun saker(@RequestBody request: InfotrygdSøkRequest): ResponseEntity> { - tilgangskontrollService.sjekkTilgang() + clientValidator.authorizeClient() val brukere = request.brukere.map { FoedselsNr(it) } val barn = request.barn?.takeUnless { it.isEmpty() }?.map { FoedselsNr(it) } @@ -90,7 +90,7 @@ class BarnetrygdController( @Operation(summary = "Teller antall migreringer igjen fra side i input") @PostMapping(path = ["migrering/antall"]) fun tellKlarTilMigrering(@RequestBody request: MigreringRequest): ResponseEntity { - tilgangskontrollService.sjekkTilgang() + clientValidator.authorizeClient() val result = barnetrygdService.finnPersonerKlarForMigrering( request.page, @@ -118,7 +118,7 @@ class BarnetrygdController( @Operation(summary = "Uttrekk personer med ytelse. F.eks OS OS for barnetrygd, UT EF for småbarnstillegg") @PostMapping(path = ["migrering/v2"]) fun migreringV2(@RequestBody request: MigreringRequest): ResponseEntity { - tilgangskontrollService.sjekkTilgang() + clientValidator.authorizeClient() return ResponseEntity.ok( barnetrygdService.finnPersonerKlarForMigrering( @@ -135,7 +135,7 @@ class BarnetrygdController( @GetMapping(path = ["stonad/{id}"]) @Deprecated(message="Erstattes av findStønad som henter basert på B01_PERSONKEY, B20_IVERFOM_SEQ, B20_VIRKFOM_SEQ og REGION") fun findStønadById(@PathVariable id: Long): ResponseEntity { - tilgangskontrollService.sjekkTilgang() + clientValidator.authorizeClient() try { return ResponseEntity.ok( @@ -151,7 +151,7 @@ class BarnetrygdController( @Operation(summary = "Finn stønad basert på personKey, iverksattFom, virkningFom og region") @PostMapping(path = ["stonad/sok"]) fun findStønad(@RequestBody stønadRequest: StønadRequest): ResponseEntity { - tilgangskontrollService.sjekkTilgang() + clientValidator.authorizeClient() try { return ResponseEntity.ok( @@ -172,7 +172,7 @@ class BarnetrygdController( @Operation(summary = "Finn om brev med brevkode er sendt for en person i forrige måned") @PostMapping(path = ["/brev"]) fun harSendtBrevForrigeMåned(@RequestBody sendtBrevRequest: SendtBrevRequest): ResponseEntity { - tilgangskontrollService.sjekkTilgang() + clientValidator.authorizeClient() val listeMedBrevhendelser = barnetrygdService.harSendtBrevForrigeMåned( sendtBrevRequest.personidenter.map { FoedselsNr(it)}, diff --git a/src/main/kotlin/no/nav/familie/ba/infotrygd/rest/controller/BisysController.kt b/src/main/kotlin/no/nav/familie/ba/infotrygd/rest/controller/BisysController.kt index 8597da89..e48007b5 100644 --- a/src/main/kotlin/no/nav/familie/ba/infotrygd/rest/controller/BisysController.kt +++ b/src/main/kotlin/no/nav/familie/ba/infotrygd/rest/controller/BisysController.kt @@ -7,8 +7,8 @@ import io.swagger.v3.oas.annotations.media.ExampleObject import io.swagger.v3.oas.annotations.media.Schema import no.nav.commons.foedselsnummer.FoedselsNr import no.nav.familie.ba.infotrygd.service.BarnetrygdService -import no.nav.familie.ba.infotrygd.service.TilgangskontrollService -import no.nav.security.token.support.core.api.ProtectedWithClaims +import no.nav.familie.ba.infotrygd.service.ClientValidator +import no.nav.security.token.support.core.api.Protected import org.slf4j.LoggerFactory import org.springframework.http.HttpStatus import org.springframework.web.bind.annotation.PostMapping @@ -21,13 +21,13 @@ import java.time.YearMonth import io.swagger.v3.oas.annotations.parameters.RequestBody as ApiRequestBody -@ProtectedWithClaims(issuer = "azuread") +@Protected @RestController @Timed(value = "infotrygd_historikk_bisys_controller", percentiles = [0.5, 0.95]) @RequestMapping("/infotrygd/barnetrygd") class BisysController( private val barnetrygdService: BarnetrygdService, - private val tilgangskontrollService: TilgangskontrollService + private val clientValidator: ClientValidator ) { private val logger = LoggerFactory.getLogger(javaClass) @@ -35,7 +35,7 @@ class BisysController( @PostMapping(path = ["utvidet"], consumes = ["application/json"]) @ApiRequestBody(content = [Content(examples = [ExampleObject(value = """{"personIdent": "12345678910", "fraDato": "2020-05"}""")])]) fun utvidet(@RequestBody request: InfotrygdUtvidetBarnetrygdRequest): InfotrygdUtvidetBarnetrygdResponse { - tilgangskontrollService.sjekkTilgang() + clientValidator.authorizeClient() if (request.fraDato.isBefore(YearMonth.now().minusYears(5))) throw ResponseStatusException(HttpStatus.BAD_REQUEST, "fraDato kan ikke være lenger enn 5 år tilbake i tid") diff --git a/src/main/kotlin/no/nav/familie/ba/infotrygd/rest/controller/PensjonController.kt b/src/main/kotlin/no/nav/familie/ba/infotrygd/rest/controller/PensjonController.kt index 42182f86..54ac5149 100644 --- a/src/main/kotlin/no/nav/familie/ba/infotrygd/rest/controller/PensjonController.kt +++ b/src/main/kotlin/no/nav/familie/ba/infotrygd/rest/controller/PensjonController.kt @@ -9,9 +9,8 @@ import io.swagger.v3.oas.annotations.media.ExampleObject import io.swagger.v3.oas.annotations.media.Schema import no.nav.commons.foedselsnummer.FoedselsNr import no.nav.familie.ba.infotrygd.service.BarnetrygdService -import no.nav.familie.ba.infotrygd.service.TilgangskontrollService +import no.nav.familie.ba.infotrygd.service.ClientValidator import no.nav.security.token.support.core.api.Protected -import no.nav.security.token.support.core.api.ProtectedWithClaims import org.springframework.http.HttpStatus import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PostMapping @@ -24,20 +23,20 @@ import java.time.LocalDate import java.time.YearMonth import io.swagger.v3.oas.annotations.parameters.RequestBody as ApiRequestBody -@ProtectedWithClaims(issuer = "azuread") +@Protected @RestController @Timed(value = "infotrygd_historikk_pensjon_controller", percentiles = [0.5, 0.95]) @RequestMapping("/infotrygd/barnetrygd") class PensjonController( private val barnetrygdService: BarnetrygdService, - private val tilgangskontrollService: TilgangskontrollService, + private val clientValidator: ClientValidator, ) { @Operation(summary = "Uttrekk barnetrygdperioder på en person fra en bestemet måned. Maks 2 år tilbake i tid") @PostMapping(path = ["pensjon"], consumes = ["application/json"]) @ApiRequestBody(content = [Content(examples = [ExampleObject(value = """{"ident": "12345678910", "fraDato": "2022-12-01"}""")])]) fun hentBarnetrygd(@RequestBody request: BarnetrygdTilPensjonRequest): BarnetrygdTilPensjonResponse { - tilgangskontrollService.sjekkTilgang() + clientValidator.authorizeClient() val fraDato = YearMonth.of(request.fraDato.year, request.fraDato.month) @@ -55,7 +54,7 @@ class PensjonController( @Operation(summary = "Finner alle personer med barnetrygd innenfor et bestemt år på vegne av Psys") @GetMapping(path = ["pensjon"]) fun personerMedBarnetrygd(@Parameter(name = "aar") @RequestParam("aar") år: String): List { - tilgangskontrollService.sjekkTilgang() + clientValidator.authorizeClient() return barnetrygdService.finnPersonerBarnetrygdPensjon(år) } diff --git a/src/main/kotlin/no/nav/familie/ba/infotrygd/rest/controller/SkatteetatenController.kt b/src/main/kotlin/no/nav/familie/ba/infotrygd/rest/controller/SkatteetatenController.kt index e6eb0458..4e6dbf66 100644 --- a/src/main/kotlin/no/nav/familie/ba/infotrygd/rest/controller/SkatteetatenController.kt +++ b/src/main/kotlin/no/nav/familie/ba/infotrygd/rest/controller/SkatteetatenController.kt @@ -8,13 +8,13 @@ import io.swagger.v3.oas.annotations.media.ExampleObject import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import no.nav.familie.ba.infotrygd.service.BarnetrygdService -import no.nav.familie.ba.infotrygd.service.TilgangskontrollService +import no.nav.familie.ba.infotrygd.service.ClientValidator import no.nav.familie.eksterne.kontrakter.skatteetaten.SkatteetatenPeriode import no.nav.familie.eksterne.kontrakter.skatteetaten.SkatteetatenPerioderRequest import no.nav.familie.eksterne.kontrakter.skatteetaten.SkatteetatenPerioderResponse import no.nav.familie.eksterne.kontrakter.skatteetaten.SkatteetatenPersonerResponse import no.nav.familie.log.mdc.MDCConstants -import no.nav.security.token.support.core.api.ProtectedWithClaims +import no.nav.security.token.support.core.api.Protected import org.slf4j.LoggerFactory import org.slf4j.MDC import org.springframework.web.bind.annotation.GetMapping @@ -27,13 +27,13 @@ import java.util.UUID import io.swagger.v3.oas.annotations.parameters.RequestBody as ApiRequestBody -@ProtectedWithClaims(issuer = "azuread") +@Protected @RestController @Timed(value = "infotrygd_historikk_skatt_controller", percentiles = [0.5, 0.95]) @RequestMapping("/infotrygd/barnetrygd") class SkatteetatenController( private val barnetrygdService: BarnetrygdService, - private val tilgangskontrollService: TilgangskontrollService + private val clientValidator: ClientValidator ) { private val logger = LoggerFactory.getLogger(javaClass) @@ -47,7 +47,7 @@ class SkatteetatenController( @RequestBody request: SkatteetatenPerioderRequest ): List { - tilgangskontrollService.sjekkTilgang() + clientValidator.authorizeClient() return request.identer.map { barnetrygdService.finnPerioderUtvidetBarnetrygdSkatt(it, request.aar.toInt()) @@ -57,14 +57,14 @@ class SkatteetatenController( @Operation(summary = "Finner alle personer med utvidet barnetrygd innenfor et bestemt år") @GetMapping(path = ["utvidet"]) fun personerMedUtvidet(@Parameter(name = "aar") @RequestParam("aar") år: String): SkatteetatenPersonerResponse { - tilgangskontrollService.sjekkTilgang() + clientValidator.authorizeClient() return SkatteetatenPersonerResponse(brukere = barnetrygdService.finnPersonerUtvidetBarnetrygdSkatt(år)) } @Operation(summary = "Finner alle personer med utvidet barnetrygd innenfor et bestemt år") @GetMapping(path = ["delingsprosent"]) fun identifiserAntallUsikkerDelingsprosent(@Parameter(name = "aar") @RequestParam("aar") år: String): String { - tilgangskontrollService.sjekkTilgang() + clientValidator.authorizeClient() val allePersoner = personerMedUtvidet(år).brukere diff --git a/src/main/kotlin/no/nav/familie/ba/infotrygd/service/ClientValidator.kt b/src/main/kotlin/no/nav/familie/ba/infotrygd/service/ClientValidator.kt new file mode 100644 index 00000000..be5afa96 --- /dev/null +++ b/src/main/kotlin/no/nav/familie/ba/infotrygd/service/ClientValidator.kt @@ -0,0 +1,100 @@ +package no.nav.familie.ba.infotrygd.service + +import no.nav.familie.ba.infotrygd.Profiles +import no.nav.security.token.support.core.context.TokenValidationContext +import no.nav.security.token.support.core.context.TokenValidationContextHolder +import org.slf4j.LoggerFactory +import org.springframework.beans.factory.annotation.Value +import org.springframework.core.env.Environment +import org.springframework.http.HttpStatus +import org.springframework.stereotype.Component +import org.springframework.web.server.ResponseStatusException + + +@Component +class ClientValidator( + private val environment: Environment, + private val ctxHolder: TokenValidationContextHolder?, + + @Value("\${rolle.teamfamilie.forvalter}") + private val forvalterRolleTeamfamilie: String, + @Value("\${no.nav.security.jwt.issuer.azure.accepted_audience}") + private val audience: String, + @Value("\${app.security.clientWhitelist}") + clientWhitelistStr: String +) { + private val logger = LoggerFactory.getLogger(javaClass) + private val clientWhitelist = clientWhitelistStr.split(',').toSet() + + fun authorizeClient() { + if(!authorized()) { + val msg = "Klienten er ikke autorisert: ${issuerSubjects().plus(azureClientIds())} \nRoller: ${azureGroups()}" + logger.info(msg) + throw ResponseStatusException(HttpStatus.UNAUTHORIZED, msg) + } + } + + private fun authorized(): Boolean { + if(environment.acceptsProfiles(Profiles.NOAUTH)) { + return true + } + + val subjects = issuerSubjects() + for(entry in clientWhitelist) { + if(subjects.contains(entry)) { + return true + } + } + + val azureClientIds = azureClientIds() + for (entry in clientWhitelist) { + if (azureClientIds.contains(entry)) { + return true + } else if (azureClientIds.contains("$AzureIssuer/$audience")) { // true for tokens opprettet via OpenAPI-flyt + return forvalterRolleTeamfamilie.isNotEmpty() && azureGroups().any { it.contains(forvalterRolleTeamfamilie) } + } + } + + return false + } + + private fun issuerSubjects(): List { + val oidcValidationContext: TokenValidationContext = ctxHolder?.tokenValidationContext + ?: return emptyList() + + return oidcValidationContext.issuers.map { + val subject = oidcValidationContext.getClaims(it).subject + "$it/$subject" + } + } + + private fun azureClientIds() : List { + val oidcValidationContext: TokenValidationContext = ctxHolder?.tokenValidationContext + ?: return emptyList() + + return oidcValidationContext.issuers + .filter { it == AzureIssuer } + .map { oidcValidationContext.getClaims(it) } + .filterNotNull() + .filter { it.get(AzureV2ClientIdClaim) != null || it.get(AzureV1ClientIdClaim) != null } + .map { "${AzureIssuer}/${it.get(AzureV2ClientIdClaim)?:it.get(AzureV1ClientIdClaim)!!}" } + } + + private fun azureGroups(): List { + val oidcValidationContext: TokenValidationContext = ctxHolder?.tokenValidationContext + ?: return emptyList() + + return oidcValidationContext.issuers + .filter { it == AzureIssuer } + .map { oidcValidationContext.getClaims(it) } + .filterNotNull() + .map { "${it.get("groups")}" } + } + + private companion object { + private const val AzureIssuer = "azure" + // https://docs.microsoft.com/en-us/azure/active-directory/develop/access-tokens#payload-claims + private const val AzureV1ClientIdClaim = "appid" + private const val AzureV2ClientIdClaim = "azp" + } +} \ No newline at end of file diff --git a/src/main/kotlin/no/nav/familie/ba/infotrygd/service/TilgangskontrollService.kt b/src/main/kotlin/no/nav/familie/ba/infotrygd/service/TilgangskontrollService.kt deleted file mode 100644 index f5285114..00000000 --- a/src/main/kotlin/no/nav/familie/ba/infotrygd/service/TilgangskontrollService.kt +++ /dev/null @@ -1,30 +0,0 @@ -package no.nav.familie.ba.infotrygd.service -import no.nav.security.token.support.core.context.TokenValidationContextHolder -import org.springframework.beans.factory.annotation.Value -import org.springframework.http.HttpStatus -import org.springframework.stereotype.Service -import org.springframework.web.server.ResponseStatusException - -@Service -class TilgangskontrollService( - private val tokenValidationContextHolder: TokenValidationContextHolder, - @Value("\${TEAMFAMILIE_FORVALTNING_GROUP_ID}") private val forvalterGroupId: String -) { - - fun sjekkTilgang() { - val roles = tokenValidationContextHolder.tokenValidationContext.anyValidClaims.map { - it.getAsList("roles") - }.orElse(emptyList()) - val groups = tokenValidationContextHolder.tokenValidationContext.anyValidClaims.map { - it.getAsList("groups") - }.orElse(emptyList()) - - if (!roles.contains(ACCESS_AS_APPLICATION_ROLE) && !groups.contains(forvalterGroupId)) { - throw ResponseStatusException(HttpStatus.FORBIDDEN, "Bruker har ikke tilgang til å kalle tjenesten!") - } - } - - companion object { - const val ACCESS_AS_APPLICATION_ROLE = "access_as_application" - } -} \ No newline at end of file diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 1875390c..2b4d6f43 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -1,5 +1,3 @@ -# Swagger -AUTHORIZATION_URL: https://login.microsoftonline.com/navno.onmicrosoft.com/oauth2/v2.0/authorize -TOKEN_URL: https://login.microsoftonline.com/navno.onmicrosoft.com/oauth2/v2.0/token - -TEAMFAMILIE_FORVALTNING_GROUP_ID: "3d718ae5-f25e-47a4-b4b3-084a97604c1d" \ No newline at end of file +rolle: + teamfamilie: + forvalter: "3d718ae5-f25e-47a4-b4b3-084a97604c1d" \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index c12f90ae..a58f9630 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -36,11 +36,19 @@ http.proxy.parametername: notused no.nav.security.jwt: expirythreshold: 60 #threshold in minutes until token expires issuer: - azuread: + azure: discoveryurl: ${AZURE_APP_WELL_KNOWN_URL} accepted_audience: ${AZURE_APP_CLIENT_ID} proxyurl: ${APP_AZURE_PROXY_URL} +app: + security: + clientWhitelist: ${APP_CLIENT_WHITELIST} + +rolle: + teamfamilie: + forvalter: "928636f4-fd0d-4149-978e-a6fb68bb19de" + springdoc: packages-to-scan: "no.nav.familie.ba.infotrygd" paths-to-match: "/infotrygd/**" @@ -51,9 +59,5 @@ springdoc: scope-separator: "," disable-swagger-default-url: true -# Swagger -AUTHORIZATION_URL: https://login.microsoftonline.com/navq.onmicrosoft.com/oauth2/v2.0/authorize -TOKEN_URL: https://login.microsoftonline.com/navq.onmicrosoft.com/oauth2/v2.0/token +AUTHORIZATION_URL: https://login.microsoftonline.com/${AZURE_APP_TENANT_ID}/oauth2/v2.0/authorize API_SCOPE: api://${AZURE_APP_CLIENT_ID}/.default - -TEAMFAMILIE_FORVALTNING_GROUP_ID: "928636f4-fd0d-4149-978e-a6fb68bb19de" \ No newline at end of file diff --git a/src/test/kotlin/no/nav/familie/ba/infotrygd/MainTest.kt b/src/test/kotlin/no/nav/familie/ba/infotrygd/MainTest.kt index f762bad0..89d6f736 100644 --- a/src/test/kotlin/no/nav/familie/ba/infotrygd/MainTest.kt +++ b/src/test/kotlin/no/nav/familie/ba/infotrygd/MainTest.kt @@ -1,14 +1,12 @@ package no.nav.familie.ba.infotrygd -import no.nav.familie.ba.infotrygd.testutil.TestClient +import no.nav.familie.ba.infotrygd.testutil.restClientNoAuth import org.assertj.core.api.Assertions.assertThat import org.junit.Test import org.junit.runner.RunWith -import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest import org.springframework.boot.web.server.LocalServerPort import org.springframework.http.HttpStatus -import org.springframework.http.ResponseEntity import org.springframework.test.context.ActiveProfiles import org.springframework.test.context.junit4.SpringRunner @@ -18,10 +16,7 @@ import org.springframework.test.context.junit4.SpringRunner class MainTest { @LocalServerPort - var port: Int = 0 - - @Autowired - private lateinit var testClient: TestClient + var port: kotlin.Int = 0 @Test fun contextLoads() { @@ -29,9 +24,12 @@ class MainTest { @Test fun health() { - val response = testClient.restTemplateNoAuth(port) - .getForEntity("/actuator/health", Any::class.java) as ResponseEntity<*> + val response = restClientNoAuth(port) + .get() + .uri("/actuator/health") + .exchange() + .block() !! - assertThat(response.statusCode).isEqualTo(HttpStatus.OK) + assertThat(response.statusCode()).isEqualTo(HttpStatus.OK) } } diff --git a/src/test/kotlin/no/nav/familie/ba/infotrygd/SecurityConfigurationDev.kt b/src/test/kotlin/no/nav/familie/ba/infotrygd/SecurityConfigurationDev.kt index a0640fba..3f3920ff 100644 --- a/src/test/kotlin/no/nav/familie/ba/infotrygd/SecurityConfigurationDev.kt +++ b/src/test/kotlin/no/nav/familie/ba/infotrygd/SecurityConfigurationDev.kt @@ -1,12 +1,11 @@ package no.nav.familie.ba.infotrygd -import no.nav.security.token.support.spring.test.MockLoginController -import no.nav.security.token.support.spring.test.MockOAuth2ServerAutoConfiguration +import no.nav.security.token.support.test.spring.TokenGeneratorConfiguration import org.springframework.context.annotation.Configuration import org.springframework.context.annotation.Import import org.springframework.context.annotation.Profile @Configuration @Profile("test") -@Import(MockOAuth2ServerAutoConfiguration::class, MockLoginController::class) +@Import(TokenGeneratorConfiguration::class) class SecurityConfigurationDev diff --git a/src/test/kotlin/no/nav/familie/ba/infotrygd/rest/controller/BarnetrygdControllerTest.kt b/src/test/kotlin/no/nav/familie/ba/infotrygd/rest/controller/BarnetrygdControllerTest.kt index aca777f7..8410d88a 100644 --- a/src/test/kotlin/no/nav/familie/ba/infotrygd/rest/controller/BarnetrygdControllerTest.kt +++ b/src/test/kotlin/no/nav/familie/ba/infotrygd/rest/controller/BarnetrygdControllerTest.kt @@ -21,27 +21,26 @@ import no.nav.familie.ba.infotrygd.rest.api.InfotrygdSøkRequest import no.nav.familie.ba.infotrygd.rest.api.InfotrygdÅpenSakResponse import no.nav.familie.ba.infotrygd.rest.controller.BarnetrygdController.StønadRequest import no.nav.familie.ba.infotrygd.service.BarnetrygdService -import no.nav.familie.ba.infotrygd.testutil.TestClient import no.nav.familie.ba.infotrygd.testutil.TestData +import no.nav.familie.ba.infotrygd.testutil.restClient +import no.nav.familie.ba.infotrygd.testutil.restClientNoAuth import no.nav.familie.kontrakter.ba.infotrygd.InfotrygdSøkResponse import no.nav.familie.kontrakter.ba.infotrygd.Sak import no.nav.familie.kontrakter.felles.objectMapper import org.assertj.core.api.Assertions.assertThat -import org.junit.Before import org.junit.Test -import org.junit.jupiter.api.assertThrows import org.junit.runner.RunWith import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest import org.springframework.boot.web.server.LocalServerPort import org.springframework.http.HttpStatus +import org.springframework.http.MediaType import org.springframework.test.context.ActiveProfiles import org.springframework.test.context.junit4.SpringRunner -import org.springframework.web.client.RestTemplate -import org.springframework.web.server.ResponseStatusException +import org.springframework.web.reactive.function.client.ClientResponse +import org.springframework.web.reactive.function.client.WebClient import no.nav.familie.kontrakter.ba.infotrygd.Stønad as StønadDto - @RunWith(SpringRunner::class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @ActiveProfiles("test") @@ -50,11 +49,6 @@ class BarnetrygdControllerTest { @LocalServerPort private var port: Int = 0 - @Autowired - private lateinit var testClient: TestClient - - private lateinit var restTemplate: RestTemplate - @Autowired lateinit var personRepository: PersonRepository @@ -96,11 +90,6 @@ class BarnetrygdControllerTest { "lopende-barnetrygd" to "/infotrygd/barnetrygd/lopende-barnetrygd", "aapen-sak" to "/infotrygd/barnetrygd/aapen-sak") - @Before - fun init() { - restTemplate = testClient.restTemplate(port) - } - @Test fun `infotrygdsøk etter løpende barnetrygd`() { val (person, opphørPerson) = personRepository.saveAll(listOf(1,2).map { TestData.person() }) @@ -114,17 +103,17 @@ class BarnetrygdControllerTest { val requestMedBarnTilknyttetLøpendeStønad = InfotrygdSøkRequest(listOf(opphørPerson.fnr), listOf(barn.barnFnr)) val requestMedBarnSomIkkeFinnes = InfotrygdSøkRequest(listOf(), listOf(person.fnr)) - assertThat(post(requestMedPersonMedLøpendeStønad, uri["lopende-barnetrygd"], InfotrygdLøpendeBarnetrygdResponse::class.java) + assertThat(post(requestMedPersonMedLøpendeStønad, uri["lopende-barnetrygd"]).pakkUt(InfotrygdLøpendeBarnetrygdResponse::class.java) .harLøpendeBarnetrygd).isTrue - assertThat(post(requestMedPersonMedLøpendeStønad, uri["stønad"], InfotrygdSøkResponse::class.java).bruker) + assertThat(post(requestMedPersonMedLøpendeStønad, uri["stønad"]).pakkUt(InfotrygdSøkResponse::class.java).bruker) .isNotEmpty - assertThat(post(requestMedPersonMedOpphørtStønad, uri["stønad"], InfotrygdSøkResponse::class.java)).extracting("bruker", "barn") + assertThat(post(requestMedPersonMedOpphørtStønad, uri["stønad"]).pakkUt(InfotrygdSøkResponse::class.java)).extracting("bruker", "barn") .containsOnly(emptyList()) - assertThat(post(requestMedBarnTilknyttetLøpendeStønad, uri["stønad"], InfotrygdSøkResponse::class.java).barn) + assertThat(post(requestMedBarnTilknyttetLøpendeStønad, uri["stønad"]).pakkUt(InfotrygdSøkResponse::class.java).barn) .isNotEmpty - assertThat(post(requestMedBarnSomIkkeFinnes, uri["stønad"], InfotrygdSøkResponse::class.java)).extracting("bruker", "barn") + assertThat(post(requestMedBarnSomIkkeFinnes, uri["stønad"]).pakkUt(InfotrygdSøkResponse::class.java)).extracting("bruker", "barn") .containsOnly(emptyList()) - assertThat(post(uri = uri["stønad"], responseType = InfotrygdSøkResponse::class.java)).extracting("bruker", "barn") + assertThat(post(uri = uri["stønad"]).pakkUt(InfotrygdSøkResponse::class.java)).extracting("bruker", "barn") .containsOnly(emptyList()) } @@ -138,15 +127,15 @@ class BarnetrygdControllerTest { val søkPåPersonMedSak = InfotrygdSøkRequest(listOf(person.fnr)) val søkPåBarnTilknyttetSak = InfotrygdSøkRequest(listOf(), listOf(barn.barnFnr)) - assertThat(post(søkPåPersonMedSak, uri["sak"], InfotrygdSøkResponse::class.java)).extracting { + assertThat(post(søkPåPersonMedSak, uri["sak"]).pakkUt(InfotrygdSøkResponse::class.java)).extracting { it -> it.bruker.map { objectMapper.convertValue(it, Sak::class.java) } }.isEqualToComparingFieldByFieldRecursively(listOf(barnetrygdService.konverterTilDto(sak))) - assertThat(post(søkPåBarnTilknyttetSak, uri["sak"], InfotrygdSøkResponse::class.java)).extracting { + assertThat(post(søkPåBarnTilknyttetSak, uri["sak"]).pakkUt(InfotrygdSøkResponse::class.java)).extracting { it -> it.barn.map { objectMapper.convertValue(it, Sak::class.java) } }.isEqualToComparingFieldByFieldRecursively(listOf(barnetrygdService.konverterTilDto(sak))) - assertThat(post(uri = uri["sak"], responseType = InfotrygdSøkResponse::class.java).bruker) // søk med tom request + assertThat(post(uri = uri["sak"]).pakkUt(InfotrygdSøkResponse::class.java).bruker) // søk med tom request .isEmpty() } @@ -164,12 +153,12 @@ class BarnetrygdControllerTest { val søkRequest = InfotrygdSøkRequest(listOf(person.fnr), emptyList()) - assertThat(post(søkRequest, uri["aapen-sak"], InfotrygdÅpenSakResponse::class.java).harÅpenSak) + assertThat(post(søkRequest, uri["aapen-sak"]).pakkUt(InfotrygdÅpenSakResponse::class.java).harÅpenSak) .isTrue beslutningRepository.saveAndFlush(Beslutning(1, vedtak.vedtakId, "J")) - assertThat(post(søkRequest, uri["aapen-sak"], InfotrygdÅpenSakResponse::class.java).harÅpenSak) + assertThat(post(søkRequest, uri["aapen-sak"]).pakkUt(InfotrygdÅpenSakResponse::class.java).harÅpenSak) .isFalse } @@ -183,8 +172,8 @@ class BarnetrygdControllerTest { utbetalingRepository.saveAll(stønader.map { TestData.utbetaling(it) }) } - get("/infotrygd/barnetrygd/utvidet?aar=2020", - BisysController.InfotrygdUtvidetBaPersonerResponse::class.java).also { + get("/infotrygd/barnetrygd/utvidet?aar=2020") + .pakkUt(BisysController.InfotrygdUtvidetBaPersonerResponse::class.java).also { assertThat(it.brukere).hasSize(2) } } @@ -199,16 +188,15 @@ class BarnetrygdControllerTest { val stønad = stønadRepository.saveAndFlush(TestData.stønad(person)) post( - uri = "/infotrygd/barnetrygd/stonad/sok", - request = StønadRequest( + "/infotrygd/barnetrygd/stonad/sok", + StønadRequest( person.fnr.asString, stønad.tkNr, stønad.iverksattFom, stønad.virkningFom, stønad.region - ), - responseType = StønadDto::class.java - ).also { + ) + ).pakkUt(StønadDto::class.java).also { assertThat(it.id).isEqualTo(stønad.id) } } @@ -219,32 +207,62 @@ class BarnetrygdControllerTest { TestData.stønad(TestData.person(), virkningFom = (999999-201901).toString(), status = "01"), // ordinær barnetrygd fra 2019 ) - val response = assertThrows { - get("/infotrygd/barnetrygd/stonad/666", Any::class.java) - } - assertThat(response.status).isEqualTo(HttpStatus.NOT_FOUND) + val response = get("/infotrygd/barnetrygd/stonad/666") + assertThat(response.statusCode()).isEqualTo(HttpStatus.NOT_FOUND) } @Test fun noAuth() { uri.values.forEach { - val restTemplate = testClient.restTemplateNoAuth(port) - val result = assertThrows { - post(uri = it, restTemplate = restTemplate, responseType = InfotrygdSøkResponse::class.java) - } - assertThat(result.status).isEqualTo(HttpStatus.UNAUTHORIZED) + val client = restClientNoAuth(port) + val result = post(uri = it, client = client) + assertThat(result.statusCode()).isEqualTo(HttpStatus.UNAUTHORIZED) + } + } + + @Test + fun clientAuth() { + uri.values.forEach { + val client = restClient(port, subject = "wrong") + val result = post(uri = it, client = client) + assertThat(result.statusCode()).isEqualTo(HttpStatus.UNAUTHORIZED) } } - private fun post( - request: Any = InfotrygdSøkRequest(listOf()), + private fun post( + request: InfotrygdSøkRequest = InfotrygdSøkRequest(listOf()), uri: String?, - responseType: Class, - restTemplate: RestTemplate = this.restTemplate, - ) = restTemplate.postForEntity(uri!!, request, responseType).body!! + client: WebClient = restClient(port), + ): ClientResponse { + return client.post() + .uri(uri!!) + .contentType(MediaType.APPLICATION_JSON) + .syncBody(request) + .exchange() + .block()!! + } - private fun get( + private fun post( uri: String?, - responseType: Class - ) = restTemplate.getForEntity(uri!!, responseType).body!! + stønadRequest: StønadRequest + ): ClientResponse { + return restClient(port).post() + .uri(uri!!) + .contentType(MediaType.APPLICATION_JSON) + .syncBody(stønadRequest) + .exchange() + .block()!! + } + + private fun get(uri: String?): ClientResponse { + return restClient(port) + .get() + .uri(uri!!) + .exchange() + .block() !! + } } + +private fun ClientResponse.pakkUt(type: Class): T { + return this.bodyToMono(type).block()!! +} \ No newline at end of file diff --git a/src/test/kotlin/no/nav/familie/ba/infotrygd/service/ClientValidatorTest.kt b/src/test/kotlin/no/nav/familie/ba/infotrygd/service/ClientValidatorTest.kt new file mode 100644 index 00000000..eaa259ff --- /dev/null +++ b/src/test/kotlin/no/nav/familie/ba/infotrygd/service/ClientValidatorTest.kt @@ -0,0 +1,108 @@ +package no.nav.familie.ba.infotrygd.service + +import com.nimbusds.jwt.JWTClaimsSet +import no.nav.familie.ba.infotrygd.Profiles +import no.nav.security.token.support.core.context.TokenValidationContext +import no.nav.security.token.support.core.context.TokenValidationContextHolder +import no.nav.security.token.support.core.jwt.JwtTokenClaims +import org.junit.Test +import org.junit.runner.RunWith +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.mock.mockito.MockBean +import org.springframework.core.env.Environment +import org.springframework.test.context.ContextConfiguration +import org.springframework.test.context.TestPropertySource +import org.springframework.test.context.junit4.SpringRunner +import org.mockito.Mockito.`when` +import org.springframework.web.server.ResponseStatusException + + +@RunWith(SpringRunner::class) +@ContextConfiguration(classes = [ClientValidator::class]) +@TestPropertySource(properties = [ + "app.security.clientWhitelist=sts/sub-claim,azure/sub-claim,azure/azp-claim,azure/appid-claim" +]) +internal class ClientValidatorTest { + + @MockBean + private lateinit var environment: Environment + + @MockBean + lateinit var oidcRequestContextHolder: TokenValidationContextHolder + + @MockBean + lateinit var validationContext: TokenValidationContext + + @Autowired + lateinit var clientValidator : ClientValidator + + private fun setupMocks(issuer: String, claimKey: String, claimValue: String) { + `when`(environment.acceptsProfiles(Profiles.NOAUTH)).thenReturn(false) + + `when`(validationContext.issuers).thenReturn(listOf(issuer)) + + val claims = JwtTokenClaims( + JWTClaimsSet.Builder() + .issuer(issuer) + .claim(claimKey, claimValue) + .build()) + + `when`(validationContext.getClaims(issuer)).thenReturn(claims) + + `when`(oidcRequestContextHolder.tokenValidationContext).thenReturn(validationContext) + } + + @Test + fun `Autorisert STS token`() { + setupMocks("sts", "sub", "sub-claim") + clientValidator.authorizeClient() + } + + @Test(expected = ResponseStatusException::class) + fun `Uautorisert STS token`() { + setupMocks("sts", "sub", "ikke-whitelisted") + clientValidator.authorizeClient() + } + + @Test + fun `Autorisert Azure token (subject)`() { + setupMocks("azure", "sub", "sub-claim") + clientValidator.authorizeClient() + } + + @Test(expected = ResponseStatusException::class) + fun `Uautorisert Azure token (subject)`() { + setupMocks("azure", "sub", "ikke-whitelisted") + clientValidator.authorizeClient() + } + + @Test + fun `Autorisert AzureV1 token (clientId)`() { + setupMocks("azure", "appid", "appid-claim") + clientValidator.authorizeClient() + } + + @Test(expected = ResponseStatusException::class) + fun `Uautorisert AzureV1 token (clientId)`() { + setupMocks("azure", "appid", "ikke-whitelisted") + clientValidator.authorizeClient() + } + + @Test + fun `Autorisert AzureV2 token (clientId)`() { + setupMocks("azure", "azp", "azp-claim") + clientValidator.authorizeClient() + } + + @Test(expected = ResponseStatusException::class) + fun `Uautorisert AzureV2 token (clientId)`() { + setupMocks("azure", "azp", "ikke-whitelisted") + clientValidator.authorizeClient() + } + + @Test(expected = ResponseStatusException::class) + fun `Uautorisert Issuer`() { + setupMocks("annen-issuer", "sub", "sub-claim") + clientValidator.authorizeClient() + } +} \ No newline at end of file diff --git a/src/test/kotlin/no/nav/familie/ba/infotrygd/service/TilgangskontrollService.kt b/src/test/kotlin/no/nav/familie/ba/infotrygd/service/TilgangskontrollService.kt deleted file mode 100644 index 19e6f7b2..00000000 --- a/src/test/kotlin/no/nav/familie/ba/infotrygd/service/TilgangskontrollService.kt +++ /dev/null @@ -1,12 +0,0 @@ -package no.nav.familie.ba.infotrygd.service -import no.nav.familie.ba.infotrygd.Profiles.NOAUTH -import org.springframework.context.annotation.Profile -import org.springframework.stereotype.Service - -@Service -@Profile(NOAUTH) -class TilgangskontrollService { - fun sjekkTilgang() { - - } -} \ No newline at end of file diff --git a/src/test/kotlin/no/nav/familie/ba/infotrygd/testutil/KontantstotteClient.kt b/src/test/kotlin/no/nav/familie/ba/infotrygd/testutil/KontantstotteClient.kt new file mode 100644 index 00000000..e9b66341 --- /dev/null +++ b/src/test/kotlin/no/nav/familie/ba/infotrygd/testutil/KontantstotteClient.kt @@ -0,0 +1,28 @@ +package no.nav.familie.ba.infotrygd.testutil + +import org.springframework.web.reactive.function.client.WebClient + +fun restClient(port: Int, subject: String? = null): WebClient { + val token = authToken(port, subject) + return WebClient.builder() + .baseUrl(baseUrl(port)) + .defaultHeader("Authorization", "Bearer $token") + .build() +} + +fun restClientNoAuth(port: Int): WebClient { + return WebClient.builder() + .baseUrl("http://localhost:$port") + .build() +} + +fun authToken(port: Int, subject: String? = null): String { + val url = baseUrl(port) + val subjectQuery = subject?.let { "?subject=$it" } ?: "" + return WebClient.create("$url/local/jwt$subjectQuery").get() + .retrieve() + .bodyToMono(String::class.java) + .block() !! +} + +private fun baseUrl(port: Int) = "http://localhost:$port" \ No newline at end of file diff --git a/src/test/kotlin/no/nav/familie/ba/infotrygd/testutil/TestClient.kt b/src/test/kotlin/no/nav/familie/ba/infotrygd/testutil/TestClient.kt deleted file mode 100644 index 09a3b346..00000000 --- a/src/test/kotlin/no/nav/familie/ba/infotrygd/testutil/TestClient.kt +++ /dev/null @@ -1,118 +0,0 @@ -package no.nav.familie.ba.infotrygd.testutil - -import com.nimbusds.jose.JOSEObjectType -import no.nav.security.mock.oauth2.MockOAuth2Server -import no.nav.security.mock.oauth2.token.DefaultOAuth2TokenCallback -import org.springframework.boot.web.client.RestTemplateBuilder -import org.springframework.context.annotation.Profile -import org.springframework.http.HttpMethod -import org.springframework.http.HttpRequest -import org.springframework.http.HttpStatus -import org.springframework.http.client.ClientHttpRequestExecution -import org.springframework.http.client.ClientHttpRequestInterceptor -import org.springframework.http.client.ClientHttpResponse -import org.springframework.stereotype.Component -import org.springframework.web.client.ResponseErrorHandler -import org.springframework.web.client.RestTemplate -import org.springframework.web.server.ResponseStatusException -import java.net.URI - -@Profile("test") -@Component -class TestClient( - private val restTemplateBuilder: RestTemplateBuilder, - private val server: MockOAuth2Server -) { - - fun restTemplate(port: Int, subject: String = "12345678910"): RestTemplate { - val grupper = listOf("gruppe-123") - val roller = listOf("access_as_application") - return restTemplateBuilder(port) - .additionalInterceptors(MockOAuth2ServerAccessTokenInterceptor(grupper, roller, subject)) - .build() - } - - fun restTemplateNoAuth(port: Int) = restTemplateBuilder(port).build() - - private fun baseUrl(port: Int) = "http://localhost:$port" - - private fun restTemplateBuilder(port: Int) = restTemplateBuilder - .errorHandler(TestErrorHandler()) - .rootUri(baseUrl(port)) - .interceptors(ExceptionHandlerInterceptor()) - - private inner class TestErrorHandler : ResponseErrorHandler { - - override fun hasError(response: ClientHttpResponse): Boolean { - val rawStatusCode = response.rawStatusCode - val series = HttpStatus.Series.resolve(rawStatusCode) - return series == HttpStatus.Series.CLIENT_ERROR || series == HttpStatus.Series.SERVER_ERROR - } - - override fun handleError(response: ClientHttpResponse) { - throw IllegalArgumentException("Not implemented") - } - - override fun handleError(url: URI, method: HttpMethod, response: ClientHttpResponse) { - val status = response.statusCode - val contentType = response.headers.contentType - - val body = response.body.bufferedReader().use { it.readText() } - - throw ResponseStatusException( - status, - "Feil ved REST-kall: ${status.value()} $method - $url\n$body" - ) - } - } - - private inner class MockOAuth2ServerAccessTokenInterceptor( - private val grupper: List, - private val roller: List, - val sub: String - ) : - ClientHttpRequestInterceptor { - - override fun intercept( - request: HttpRequest, - body: ByteArray, - execution: ClientHttpRequestExecution - ): ClientHttpResponse { - val token = server.issueToken( - issuerId = "default", - "theclientid", - DefaultOAuth2TokenCallback( - issuerId = "azuread", - subject = sub, - audience = listOf("default"), - typeHeader = JOSEObjectType.JWT.type, - claims = mapOf("groups" to grupper, "roles" to roller) - ) - ) - request.headers.setBearerAuth(token.serialize()) - return execution.execute(request, body) - } - } -} - -class ExceptionHandlerInterceptor : ClientHttpRequestInterceptor { - override fun intercept( - request: HttpRequest, - body: ByteArray, - execution: ClientHttpRequestExecution - ): ClientHttpResponse { - try { - return execution.execute(request, body) - } catch (e: Exception) { - val feilmelding = - "Feil ved REST-kall ${request.method ?: ""} ${request.uri.scheme ?: ""}://${request.uri.host ?: ""}" + - ":${request.uri.port}${request.uri.path ?: ""}\n" + - "${e.message}" - throw ResponseStatusException( - HttpStatus.INTERNAL_SERVER_ERROR, - feilmelding, - e.cause - ) - } - } -} \ No newline at end of file diff --git a/src/test/resources/application-test.yml b/src/test/resources/application-test.yml index 6aad3689..9b42626f 100644 --- a/src/test/resources/application-test.yml +++ b/src/test/resources/application-test.yml @@ -18,17 +18,16 @@ spring: no.nav.security.jwt: expirythreshold: 60 #threshold in minutes until token expires issuer: - azuread: - discoveryurl: http://localhost:${mock-oauth2-server.port}/default/.well-known/openid-configuration - accepted_audience: default + azure: + discoveryurl: http://metadata + accepted_audience: aud-localhost proxyurl: ~ cookiename: localhost-idtoken -NAIS_APP_NAME: familie-ba-infotrygd +app: + security: + clientWhitelist: "azure/12345678910,sts/12345678910" -AUTHORIZATION_URL: http://localhost:${mock-oauth2-server.port}/default/authorize -TOKEN_URL: http://localhost:${mock-oauth2-server.port}/default/token -API_SCOPE: api://${AZURE_APP_CLIENT_ID} openid /.default - -AZURE_APP_CLIENT_ID: testid -AZURE_APP_CLIENT_SECRET: testsecret \ No newline at end of file +AZURE_OPENID_CONFIG_TOKEN_ENDPOINT: http://localhost:1111/v2.0/token +AUTHORIZATION_URL: http://localhost:1111/v2.0/authorize +AZURE_APP_CLIENT_ID: "" \ No newline at end of file