From dd66db897bb6912c0661562f6e30804348bb2102 Mon Sep 17 00:00:00 2001 From: tnarland <47184872+tnarland@users.noreply.github.com> Date: Wed, 25 Oct 2023 11:03:02 +0200 Subject: [PATCH] Oppdaterer sikkerhetskonfigurasjonen (#407) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Revert "Revert "Oppdaterer sikkerhetskonfigurasjonen (#394)" (#405)" This reverts commit bb9867d75ea053f0fe88299c5e3f3bdbc0019398. + Legger til saksbehandler-rollen i tilgangskontroll-sjekken slik at også de fremdeles skal ha tilgang og ruller det ut på nytt --- build.gradle.kts | 11 +- nais/dev-fss.yml | 5 +- nais/prod-fss.yml | 5 +- .../infotrygd/config/SecurityConfiguration.kt | 3 - .../ba/infotrygd/config/SwaggerConfig.kt | 14 +- .../rest/controller/BarnetrygdController.kt | 26 ++-- .../rest/controller/BisysController.kt | 10 +- .../rest/controller/PensjonController.kt | 12 +- .../rest/controller/SkatteetatenController.kt | 14 +- .../ba/infotrygd/service/ClientValidator.kt | 100 --------------- .../service/TilgangskontrollService.kt | 35 +++++ src/main/resources/application-prod.yml | 11 +- src/main/resources/application.yml | 19 ++- .../no/nav/familie/ba/infotrygd/MainTest.kt | 18 +-- .../ba/infotrygd/SecurityConfigurationDev.kt | 5 +- .../controller/BarnetrygdControllerTest.kt | 120 ++++++++---------- .../infotrygd/service/ClientValidatorTest.kt | 108 ---------------- .../service/TilgangskontrollService.kt | 12 ++ .../infotrygd/testutil/KontantstotteClient.kt | 28 ---- .../ba/infotrygd/testutil/TestClient.kt | 118 +++++++++++++++++ src/test/resources/application-test.yml | 19 +-- 21 files changed, 309 insertions(+), 384 deletions(-) delete mode 100644 src/main/kotlin/no/nav/familie/ba/infotrygd/service/ClientValidator.kt create mode 100644 src/main/kotlin/no/nav/familie/ba/infotrygd/service/TilgangskontrollService.kt delete mode 100644 src/test/kotlin/no/nav/familie/ba/infotrygd/service/ClientValidatorTest.kt create mode 100644 src/test/kotlin/no/nav/familie/ba/infotrygd/service/TilgangskontrollService.kt delete mode 100644 src/test/kotlin/no/nav/familie/ba/infotrygd/testutil/KontantstotteClient.kt create mode 100644 src/test/kotlin/no/nav/familie/ba/infotrygd/testutil/TestClient.kt diff --git a/build.gradle.kts b/build.gradle.kts index 22cb3e17..8310a807 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 tokenSupportVersion = "2.1.7" +val tokenValidationVersion = "2.1.7" val jacksonVersion = "2.9.9" val springdocVersion = "1.6.15" val navFoedselsnummerVersion = "1.0-SNAPSHOT.6" @@ -15,6 +15,7 @@ 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" @@ -76,8 +77,12 @@ 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:$tokenSupportVersion") - testImplementation("no.nav.security:token-validation-test-support:2.0.5") + 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("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 fc309546..58d2a6b3 100644 --- a/nais/dev-fss.yml +++ b/nais/dev-fss.yml @@ -81,6 +81,9 @@ 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" @@ -100,8 +103,6 @@ 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 6224f095..5b77524d 100644 --- a/nais/prod-fss.yml +++ b/nais/prod-fss.yml @@ -80,6 +80,9 @@ 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 @@ -98,8 +101,6 @@ 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 2952005d..2db7cfe2 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,11 +1,8 @@ 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 6eba5edb..37b87305 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,12 +13,14 @@ import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration @Configuration -class SwaggerConfig(@Value("\${AUTHORIZATION_URL}") - val authorizationUrl: String, - @Value("\${AZURE_OPENID_CONFIG_TOKEN_ENDPOINT}") - val tokenUrl: String, - @Value("\${API_SCOPE}") - val apiScope: String) { +class SwaggerConfig( + @Value("\${AUTHORIZATION_URL}") + val authorizationUrl: String, + @Value("\${TOKEN_URL}") + 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 79220b84..d4c87ec2 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.ClientValidator +import no.nav.familie.ba.infotrygd.service.TilgangskontrollService 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.Protected +import no.nav.security.token.support.core.api.ProtectedWithClaims 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 clientValidator: ClientValidator + private val tilgangskontrollService: TilgangskontrollService ) { 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 { - clientValidator.authorizeClient() + tilgangskontrollService.sjekkTilgang() 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 { - clientValidator.authorizeClient() + tilgangskontrollService.sjekkTilgang() 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> { - clientValidator.authorizeClient() + tilgangskontrollService.sjekkTilgang() 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> { - clientValidator.authorizeClient() + tilgangskontrollService.sjekkTilgang() 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 { - clientValidator.authorizeClient() + tilgangskontrollService.sjekkTilgang() 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 { - clientValidator.authorizeClient() + tilgangskontrollService.sjekkTilgang() 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 { - clientValidator.authorizeClient() + tilgangskontrollService.sjekkTilgang() 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 { - clientValidator.authorizeClient() + tilgangskontrollService.sjekkTilgang() 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 { - clientValidator.authorizeClient() + tilgangskontrollService.sjekkTilgang() 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 e48007b5..8597da89 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.ClientValidator -import no.nav.security.token.support.core.api.Protected +import no.nav.familie.ba.infotrygd.service.TilgangskontrollService +import no.nav.security.token.support.core.api.ProtectedWithClaims 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 -@Protected +@ProtectedWithClaims(issuer = "azuread") @RestController @Timed(value = "infotrygd_historikk_bisys_controller", percentiles = [0.5, 0.95]) @RequestMapping("/infotrygd/barnetrygd") class BisysController( private val barnetrygdService: BarnetrygdService, - private val clientValidator: ClientValidator + private val tilgangskontrollService: TilgangskontrollService ) { 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 { - clientValidator.authorizeClient() + tilgangskontrollService.sjekkTilgang() 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 95d53dc9..f2826b0a 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,8 +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.ClientValidator -import no.nav.security.token.support.core.api.Protected +import no.nav.familie.ba.infotrygd.service.TilgangskontrollService +import no.nav.security.token.support.core.api.ProtectedWithClaims import org.springframework.core.env.Environment import org.springframework.http.HttpStatus import org.springframework.web.bind.annotation.GetMapping @@ -24,13 +24,13 @@ import java.time.LocalDate import java.time.YearMonth import io.swagger.v3.oas.annotations.parameters.RequestBody as ApiRequestBody -@Protected +@ProtectedWithClaims(issuer = "azuread") @RestController @Timed(value = "infotrygd_historikk_pensjon_controller", percentiles = [0.5, 0.95]) @RequestMapping("/infotrygd/barnetrygd") class PensjonController( private val barnetrygdService: BarnetrygdService, - private val clientValidator: ClientValidator, + private val tilgangskontrollService: TilgangskontrollService, private val environment: Environment ) { @@ -38,7 +38,7 @@ class PensjonController( @PostMapping(path = ["pensjon"], consumes = ["application/json"]) @ApiRequestBody(content = [Content(examples = [ExampleObject(value = """{"ident": "12345678910", "fraDato": "2022-12-01"}""")])]) fun hentBarnetrygd(@RequestBody request: BarnetrygdTilPensjonRequest): BarnetrygdTilPensjonResponse { - clientValidator.authorizeClient() + tilgangskontrollService.sjekkTilgang() val fraDato = YearMonth.of(request.fraDato.year, request.fraDato.month) @@ -56,7 +56,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 { - clientValidator.authorizeClient() + tilgangskontrollService.sjekkTilgang() if (environment.activeProfiles.any { it == "preprod" }) { return emptyList() } 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 4e6dbf66..e6eb0458 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.ClientValidator +import no.nav.familie.ba.infotrygd.service.TilgangskontrollService 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.Protected +import no.nav.security.token.support.core.api.ProtectedWithClaims 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 -@Protected +@ProtectedWithClaims(issuer = "azuread") @RestController @Timed(value = "infotrygd_historikk_skatt_controller", percentiles = [0.5, 0.95]) @RequestMapping("/infotrygd/barnetrygd") class SkatteetatenController( private val barnetrygdService: BarnetrygdService, - private val clientValidator: ClientValidator + private val tilgangskontrollService: TilgangskontrollService ) { private val logger = LoggerFactory.getLogger(javaClass) @@ -47,7 +47,7 @@ class SkatteetatenController( @RequestBody request: SkatteetatenPerioderRequest ): List { - clientValidator.authorizeClient() + tilgangskontrollService.sjekkTilgang() 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 { - clientValidator.authorizeClient() + tilgangskontrollService.sjekkTilgang() 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 { - clientValidator.authorizeClient() + tilgangskontrollService.sjekkTilgang() 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 deleted file mode 100644 index be5afa96..00000000 --- a/src/main/kotlin/no/nav/familie/ba/infotrygd/service/ClientValidator.kt +++ /dev/null @@ -1,100 +0,0 @@ -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 new file mode 100644 index 00000000..d1dde2da --- /dev/null +++ b/src/main/kotlin/no/nav/familie/ba/infotrygd/service/TilgangskontrollService.kt @@ -0,0 +1,35 @@ +package no.nav.familie.ba.infotrygd.service +import no.nav.security.token.support.core.context.TokenValidationContextHolder +import org.slf4j.LoggerFactory +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_SAKSBEHANDLER_GROUP_ID}") private val saksbehandlerGroupId: String, + @Value("\${TEAMFAMILIE_FORVALTNING_GROUP_ID}") private val forvalterGroupId: String +) { + val secureLogger = LoggerFactory.getLogger("secureLogger") + + fun sjekkTilgang() { + val roles = tokenValidationContextHolder.tokenValidationContext.anyValidClaims.map { + it.getAsList("roles") + }.orElse(emptyList()) + val groups = tokenValidationContextHolder.tokenValidationContext.anyValidClaims.map { + it.getAsList("groups") + }.orElse(emptyList()) + + secureLogger.info("Roller: $roles") + secureLogger.info("Grupper: $groups") + if (!(roles.contains(ACCESS_AS_APPLICATION_ROLE) || roles.contains(saksbehandlerGroupId) || 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 2b4d6f43..4bc6b729 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -1,3 +1,8 @@ -rolle: - teamfamilie: - forvalter: "3d718ae5-f25e-47a4-b4b3-084a97604c1d" \ No newline at end of file +# 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" + +TEAMFAMILIE_SAKSBEHANDLER_GROUP_ID: "847e3d72-9dc1-41c3-80ff-f5d4acdd5d46" # SAKSBEHANDLER_ROLLE +TEAMFAMILIE_BESLUTTER_GROUP_ID: "7a271f87-39fb-468b-a9ee-6cf3c070f548" # BESLUTTER_ROLLE \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index a58f9630..8d1a1b95 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -36,19 +36,11 @@ http.proxy.parametername: notused no.nav.security.jwt: expirythreshold: 60 #threshold in minutes until token expires issuer: - azure: + azuread: 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/**" @@ -59,5 +51,12 @@ springdoc: scope-separator: "," disable-swagger-default-url: true -AUTHORIZATION_URL: https://login.microsoftonline.com/${AZURE_APP_TENANT_ID}/oauth2/v2.0/authorize +# 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 API_SCOPE: api://${AZURE_APP_CLIENT_ID}/.default + +TEAMFAMILIE_FORVALTNING_GROUP_ID: "928636f4-fd0d-4149-978e-a6fb68bb19de" + +TEAMFAMILIE_SAKSBEHANDLER_GROUP_ID: "d21e00a4-969d-4b28-8782-dc818abfae65" # SAKSBEHANDLER_ROLLE +TEAMFAMILIE_BESLUTTER_GROUP_ID: "9449c153-5a1e-44a7-84c6-7cc7a8867233" # BESLUTTER_ROLLE \ 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 89d6f736..f762bad0 100644 --- a/src/test/kotlin/no/nav/familie/ba/infotrygd/MainTest.kt +++ b/src/test/kotlin/no/nav/familie/ba/infotrygd/MainTest.kt @@ -1,12 +1,14 @@ package no.nav.familie.ba.infotrygd -import no.nav.familie.ba.infotrygd.testutil.restClientNoAuth +import no.nav.familie.ba.infotrygd.testutil.TestClient 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 @@ -16,7 +18,10 @@ import org.springframework.test.context.junit4.SpringRunner class MainTest { @LocalServerPort - var port: kotlin.Int = 0 + var port: Int = 0 + + @Autowired + private lateinit var testClient: TestClient @Test fun contextLoads() { @@ -24,12 +29,9 @@ class MainTest { @Test fun health() { - val response = restClientNoAuth(port) - .get() - .uri("/actuator/health") - .exchange() - .block() !! + val response = testClient.restTemplateNoAuth(port) + .getForEntity("/actuator/health", Any::class.java) as ResponseEntity<*> - 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 3f3920ff..a0640fba 100644 --- a/src/test/kotlin/no/nav/familie/ba/infotrygd/SecurityConfigurationDev.kt +++ b/src/test/kotlin/no/nav/familie/ba/infotrygd/SecurityConfigurationDev.kt @@ -1,11 +1,12 @@ package no.nav.familie.ba.infotrygd -import no.nav.security.token.support.test.spring.TokenGeneratorConfiguration +import no.nav.security.token.support.spring.test.MockLoginController +import no.nav.security.token.support.spring.test.MockOAuth2ServerAutoConfiguration import org.springframework.context.annotation.Configuration import org.springframework.context.annotation.Import import org.springframework.context.annotation.Profile @Configuration @Profile("test") -@Import(TokenGeneratorConfiguration::class) +@Import(MockOAuth2ServerAutoConfiguration::class, MockLoginController::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 8410d88a..aca777f7 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,26 +21,27 @@ 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.reactive.function.client.ClientResponse -import org.springframework.web.reactive.function.client.WebClient +import org.springframework.web.client.RestTemplate +import org.springframework.web.server.ResponseStatusException import no.nav.familie.kontrakter.ba.infotrygd.Stønad as StønadDto + @RunWith(SpringRunner::class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @ActiveProfiles("test") @@ -49,6 +50,11 @@ class BarnetrygdControllerTest { @LocalServerPort private var port: Int = 0 + @Autowired + private lateinit var testClient: TestClient + + private lateinit var restTemplate: RestTemplate + @Autowired lateinit var personRepository: PersonRepository @@ -90,6 +96,11 @@ 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() }) @@ -103,17 +114,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"]).pakkUt(InfotrygdLøpendeBarnetrygdResponse::class.java) + assertThat(post(requestMedPersonMedLøpendeStønad, uri["lopende-barnetrygd"], InfotrygdLøpendeBarnetrygdResponse::class.java) .harLøpendeBarnetrygd).isTrue - assertThat(post(requestMedPersonMedLøpendeStønad, uri["stønad"]).pakkUt(InfotrygdSøkResponse::class.java).bruker) + assertThat(post(requestMedPersonMedLøpendeStønad, uri["stønad"], InfotrygdSøkResponse::class.java).bruker) .isNotEmpty - assertThat(post(requestMedPersonMedOpphørtStønad, uri["stønad"]).pakkUt(InfotrygdSøkResponse::class.java)).extracting("bruker", "barn") + assertThat(post(requestMedPersonMedOpphørtStønad, uri["stønad"], InfotrygdSøkResponse::class.java)).extracting("bruker", "barn") .containsOnly(emptyList()) - assertThat(post(requestMedBarnTilknyttetLøpendeStønad, uri["stønad"]).pakkUt(InfotrygdSøkResponse::class.java).barn) + assertThat(post(requestMedBarnTilknyttetLøpendeStønad, uri["stønad"], InfotrygdSøkResponse::class.java).barn) .isNotEmpty - assertThat(post(requestMedBarnSomIkkeFinnes, uri["stønad"]).pakkUt(InfotrygdSøkResponse::class.java)).extracting("bruker", "barn") + assertThat(post(requestMedBarnSomIkkeFinnes, uri["stønad"], InfotrygdSøkResponse::class.java)).extracting("bruker", "barn") .containsOnly(emptyList()) - assertThat(post(uri = uri["stønad"]).pakkUt(InfotrygdSøkResponse::class.java)).extracting("bruker", "barn") + assertThat(post(uri = uri["stønad"], responseType = InfotrygdSøkResponse::class.java)).extracting("bruker", "barn") .containsOnly(emptyList()) } @@ -127,15 +138,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"]).pakkUt(InfotrygdSøkResponse::class.java)).extracting { + assertThat(post(søkPåPersonMedSak, uri["sak"], 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"]).pakkUt(InfotrygdSøkResponse::class.java)).extracting { + assertThat(post(søkPåBarnTilknyttetSak, uri["sak"], 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"]).pakkUt(InfotrygdSøkResponse::class.java).bruker) // søk med tom request + assertThat(post(uri = uri["sak"], responseType = InfotrygdSøkResponse::class.java).bruker) // søk med tom request .isEmpty() } @@ -153,12 +164,12 @@ class BarnetrygdControllerTest { val søkRequest = InfotrygdSøkRequest(listOf(person.fnr), emptyList()) - assertThat(post(søkRequest, uri["aapen-sak"]).pakkUt(InfotrygdÅpenSakResponse::class.java).harÅpenSak) + assertThat(post(søkRequest, uri["aapen-sak"], InfotrygdÅpenSakResponse::class.java).harÅpenSak) .isTrue beslutningRepository.saveAndFlush(Beslutning(1, vedtak.vedtakId, "J")) - assertThat(post(søkRequest, uri["aapen-sak"]).pakkUt(InfotrygdÅpenSakResponse::class.java).harÅpenSak) + assertThat(post(søkRequest, uri["aapen-sak"], InfotrygdÅpenSakResponse::class.java).harÅpenSak) .isFalse } @@ -172,8 +183,8 @@ class BarnetrygdControllerTest { utbetalingRepository.saveAll(stønader.map { TestData.utbetaling(it) }) } - get("/infotrygd/barnetrygd/utvidet?aar=2020") - .pakkUt(BisysController.InfotrygdUtvidetBaPersonerResponse::class.java).also { + get("/infotrygd/barnetrygd/utvidet?aar=2020", + BisysController.InfotrygdUtvidetBaPersonerResponse::class.java).also { assertThat(it.brukere).hasSize(2) } } @@ -188,15 +199,16 @@ class BarnetrygdControllerTest { val stønad = stønadRepository.saveAndFlush(TestData.stønad(person)) post( - "/infotrygd/barnetrygd/stonad/sok", - StønadRequest( + uri = "/infotrygd/barnetrygd/stonad/sok", + request = StønadRequest( person.fnr.asString, stønad.tkNr, stønad.iverksattFom, stønad.virkningFom, stønad.region - ) - ).pakkUt(StønadDto::class.java).also { + ), + responseType = StønadDto::class.java + ).also { assertThat(it.id).isEqualTo(stønad.id) } } @@ -207,62 +219,32 @@ class BarnetrygdControllerTest { TestData.stønad(TestData.person(), virkningFom = (999999-201901).toString(), status = "01"), // ordinær barnetrygd fra 2019 ) - val response = get("/infotrygd/barnetrygd/stonad/666") - assertThat(response.statusCode()).isEqualTo(HttpStatus.NOT_FOUND) - } - - @Test - fun noAuth() { - uri.values.forEach { - val client = restClientNoAuth(port) - val result = post(uri = it, client = client) - assertThat(result.statusCode()).isEqualTo(HttpStatus.UNAUTHORIZED) + val response = assertThrows { + get("/infotrygd/barnetrygd/stonad/666", Any::class.java) } + assertThat(response.status).isEqualTo(HttpStatus.NOT_FOUND) } @Test - fun clientAuth() { + fun noAuth() { uri.values.forEach { - val client = restClient(port, subject = "wrong") - val result = post(uri = it, client = client) - assertThat(result.statusCode()).isEqualTo(HttpStatus.UNAUTHORIZED) + val restTemplate = testClient.restTemplateNoAuth(port) + val result = assertThrows { + post(uri = it, restTemplate = restTemplate, responseType = InfotrygdSøkResponse::class.java) + } + assertThat(result.status).isEqualTo(HttpStatus.UNAUTHORIZED) } } - private fun post( - request: InfotrygdSøkRequest = InfotrygdSøkRequest(listOf()), + private fun post( + request: Any = InfotrygdSøkRequest(listOf()), uri: String?, - client: WebClient = restClient(port), - ): ClientResponse { - return client.post() - .uri(uri!!) - .contentType(MediaType.APPLICATION_JSON) - .syncBody(request) - .exchange() - .block()!! - } + responseType: Class, + restTemplate: RestTemplate = this.restTemplate, + ) = restTemplate.postForEntity(uri!!, request, responseType).body!! - private fun post( + private fun get( uri: String?, - 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() !! - } + responseType: Class + ) = restTemplate.getForEntity(uri!!, responseType).body!! } - -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 deleted file mode 100644 index eaa259ff..00000000 --- a/src/test/kotlin/no/nav/familie/ba/infotrygd/service/ClientValidatorTest.kt +++ /dev/null @@ -1,108 +0,0 @@ -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 new file mode 100644 index 00000000..19e6f7b2 --- /dev/null +++ b/src/test/kotlin/no/nav/familie/ba/infotrygd/service/TilgangskontrollService.kt @@ -0,0 +1,12 @@ +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 deleted file mode 100644 index e9b66341..00000000 --- a/src/test/kotlin/no/nav/familie/ba/infotrygd/testutil/KontantstotteClient.kt +++ /dev/null @@ -1,28 +0,0 @@ -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 new file mode 100644 index 00000000..09a3b346 --- /dev/null +++ b/src/test/kotlin/no/nav/familie/ba/infotrygd/testutil/TestClient.kt @@ -0,0 +1,118 @@ +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 9b42626f..6aad3689 100644 --- a/src/test/resources/application-test.yml +++ b/src/test/resources/application-test.yml @@ -18,16 +18,17 @@ spring: no.nav.security.jwt: expirythreshold: 60 #threshold in minutes until token expires issuer: - azure: - discoveryurl: http://metadata - accepted_audience: aud-localhost + azuread: + discoveryurl: http://localhost:${mock-oauth2-server.port}/default/.well-known/openid-configuration + accepted_audience: default proxyurl: ~ cookiename: localhost-idtoken -app: - security: - clientWhitelist: "azure/12345678910,sts/12345678910" +NAIS_APP_NAME: familie-ba-infotrygd -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 +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