Skip to content

Commit

Permalink
Merge pull request #493 from navikt/aktor-bytte
Browse files Browse the repository at this point in the history
Resttjeneste for aktørbytte
  • Loading branch information
espenjv authored Aug 16, 2024
2 parents b7879b2 + 3abf6ee commit 1dd2509
Show file tree
Hide file tree
Showing 15 changed files with 406 additions and 8 deletions.
16 changes: 15 additions & 1 deletion nais/dev-gcp.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,17 @@
"diskAutoresize": "false",
"highAvailability": "false"
},
"azure": {
"replyURLs": [
"https://sif-innsyn-api.intern.dev.nav.no/swagger-ui/oauth2-redirect.html"
],
"groups": [
{
"name": "0000-GA-k9-drift",
"objectId": "0bc9661c-975c-4adb-86d1-a97172490662"
}
]
},
"azureTenant": "trygdeetaten.no",
"kafkaPool": "nav-dev",
"env": {
Expand All @@ -39,7 +50,10 @@
"SAF_AZURE_SCOPE": "api://dev-fss.teamdokumenthandtering.saf-q1/.default",
"K9_SAK_INNSYN_API_TOKEN_X_AUDIENCE": "dev-gcp:dusseldorf:k9-sak-innsyn-api",
"K9_SELVBETJENING_OPPSLAG_TOKEN_X_AUDIENCE": "dev-gcp:dusseldorf:k9-selvbetjening-oppslag",
"SWAGGER_ENABLED": "true"
"SWAGGER_ENABLED": "true",
"AZURE_LOGIN_URL": "https://login.microsoftonline.com/navq.onmicrosoft.com/oauth2/v2.0",
"K9_DRIFT_GRUPPE_ID": "0bc9661c-975c-4adb-86d1-a97172490662"

},
"slack-channel": "sif-alerts-dev",
"slack-notify-type": "<!here> | sif-innsyn-api | "
Expand Down
11 changes: 11 additions & 0 deletions nais/naiserator.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,17 @@ spec:
application:
enabled: true
tenant: {{azureTenant}}
claims:
extra:
- "NAVident"
groups:
{{#each azure.groups as |group|}}
- id: {{group.objectId}}
{{/each}}
replyURLs:
{{#each azure.replyURLs as |url|}}
- {{url}}
{{/each}}
tokenx:
enabled: true
accessPolicy:
Expand Down
15 changes: 14 additions & 1 deletion nais/prod-gcp.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,17 @@
"diskAutoresize": "true",
"highAvailability": "true"
},
"azure": {
"replyURLs": [
"https://sif-innsyn-api.intern.nav.no/swagger-ui/oauth2-redirect.html"
],
"groups": [
{
"name": "0000-GA-k9-drift",
"objectId": "1509dc91-a955-4e72-b64c-2f049e37c0c6"
}
]
},
"azureTenant": "nav.no",
"kafkaPool": "nav-prod",
"env": {
Expand All @@ -38,7 +49,9 @@
"SAF_AZURE_SCOPE": "api://prod-fss.teamdokumenthandtering.saf/.default",
"SAFSELVBETJENING_TOKEN_X_AUDIENCE": "prod-fss:teamdokumenthandtering:safselvbetjening",
"K9_SAK_INNSYN_API_TOKEN_X_AUDIENCE": "prod-gcp:dusseldorf:k9-sak-innsyn-api",
"K9_SELVBETJENING_OPPSLAG_TOKEN_X_AUDIENCE": "prod-gcp:dusseldorf:k9-selvbetjening-oppslag"
"K9_SELVBETJENING_OPPSLAG_TOKEN_X_AUDIENCE": "prod-gcp:dusseldorf:k9-selvbetjening-oppslag",
"AZURE_LOGIN_URL": "https://login.microsoftonline.com/navno.onmicrosoft.com/oauth2/v2.0",
"K9_DRIFT_GRUPPE_ID": "1509dc91-a955-4e72-b64c-2f049e37c0c6"
},
"slack-channel": "sif-alerts",
"slack-notify-type": "<!here> | sif-innsyn-api | "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ internal class SecurityConfiguration

object Issuers {
const val TOKEN_X = "tokenx"
const val AZURE = "azure"
}
47 changes: 44 additions & 3 deletions src/main/kotlin/no/nav/sifinnsynapi/config/SwaggerConfiguration.kt
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
package no.nav.sifinnsynapi.config

import io.swagger.v3.oas.models.Components
import io.swagger.v3.oas.models.ExternalDocumentation
import io.swagger.v3.oas.models.OpenAPI
import io.swagger.v3.oas.models.info.Info
import io.swagger.v3.oas.models.security.*
import io.swagger.v3.oas.models.servers.Server
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.EnvironmentAware
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Profile
import org.springframework.core.env.Environment

@Configuration
@Profile("local", "dev-gcp")
class SwaggerConfiguration {
class SwaggerConfiguration(
@Value("\${springdoc.oAuthFlow.authorizationUrl}") val authorizationUrl: String,
@Value("\${springdoc.oAuthFlow.tokenUrl}") val tokenUrl: String,
@Value("\${springdoc.oAuthFlow.apiScope}") val apiScope: String
) : EnvironmentAware {

private var env: Environment? = null


@Bean
fun openAPI(): OpenAPI {
Expand All @@ -28,6 +38,37 @@ class SwaggerConfiguration {
ExternalDocumentation()
.description("Sif Innsyn Api GitHub repository")
.url("https://github.com/navikt/sif-innsyn-api")
).components(
Components()
.addSecuritySchemes("oauth2", azureLogin())
)
.addSecurityItem(
SecurityRequirement()
.addList("oauth2", listOf("read", "write"))
.addList("Authorization")
)
}


private fun azureLogin(): SecurityScheme {
return SecurityScheme()
.name("oauth2")
.type(SecurityScheme.Type.OAUTH2)
.scheme("oauth2")
.`in`(SecurityScheme.In.HEADER)
.flows(
OAuthFlows()
.authorizationCode(
OAuthFlow().authorizationUrl(authorizationUrl)
.tokenUrl(tokenUrl)
.scopes(Scopes().addString(apiScope, "read,write"))
)
)
}

override fun setEnvironment(environment: Environment) {
this.env = environment;
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package no.nav.sifinnsynapi.forvaltning

import no.nav.security.token.support.core.api.ProtectedWithClaims
import no.nav.security.token.support.core.api.RequiredIssuers
import no.nav.sifinnsynapi.common.AktørId
import no.nav.sifinnsynapi.config.Issuers
import no.nav.sifinnsynapi.sikkerhet.AuthorizationService
import no.nav.sifinnsynapi.sikkerhet.ContextHolder
import no.nav.sifinnsynapi.soknad.SøknadService
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import org.springframework.http.ProblemDetail
import org.springframework.http.ResponseEntity
import org.springframework.web.ErrorResponseException
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RestController

@RestController
@RequiredIssuers(
ProtectedWithClaims(issuer = Issuers.AZURE)
)
class AktørBytteController(
private valknadService: SøknadService,
private val authorizationService: AuthorizationService
) {

@PostMapping(
"/forvaltning/oppdaterAktoerId",
consumes = [MediaType.APPLICATION_JSON_VALUE],
produces = [MediaType.APPLICATION_JSON_VALUE]
)
@ProtectedWithClaims(issuer = ContextHolder.AZURE_AD, claimMap = ["NAVident=*"])
fun oppdaterAktoerId(@RequestBody aktørBytteRequest: AktørBytteRequest): ResponseEntity<AktørBytteRespons> {
if (!authorizationService.harTilgangTilDriftRolle()) {
val problemDetail = ProblemDetail.forStatus(HttpStatus.FORBIDDEN)
problemDetail.detail = "Mangler driftsrolle"
throw ErrorResponseException(HttpStatus.FORBIDDEN, problemDetail, null)
}
val antallOppdaterte = søknadService.oppdaterAktørId(
AktørId(aktørBytteRequest.gyldigAktør),
AktørId(aktørBytteRequest.utgåttAktør)
)
return ResponseEntity.ok(AktørBytteRespons(antallOppdaterte))
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package no.nav.sifinnsynapi.forvaltning

import com.fasterxml.jackson.annotation.JsonProperty
import jakarta.validation.constraints.Pattern
import jakarta.validation.constraints.Size
import org.jetbrains.annotations.NotNull

data class AktørBytteRequest(
@JsonProperty
@NotNull
@Size(max = 20)
@Pattern(regexp = "^\\d+$", message = "AktørId [\${validatedValue}] matcher ikke tillatt pattern [{regexp}]")
val utgåttAktør: String,
@JsonProperty
@NotNull
@Size(max = 20)
@Pattern(regexp = "^\\d+$", message = "AktørId [\${validatedValue}] matcher ikke tillatt pattern [{regexp}]")
val gyldigAktør: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package no.nav.sifinnsynapi.forvaltning

import com.fasterxml.jackson.annotation.JsonProperty
import jakarta.validation.constraints.Pattern
import jakarta.validation.constraints.Size
import org.jetbrains.annotations.NotNull

data class AktørBytteRespons(
@JsonProperty
@NotNull
val antallOppdaterteRader: Int,

)
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package no.nav.sifinnsynapi.sikkerhet

import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

class AuthorizationService(
private val contextHolder: ContextHolder,
private val k9DriftGruppeId: String,
) {
fun harTilgangTilDriftRolle(): Boolean {
return contextHolder.requestKontekst()?.jwtToken?.containsClaim("groups", k9DriftGruppeId) ?: false
}
}

@Configuration
class AuthorizationConfig(
@Value("\${no.nav.security.k9-drift-gruppe}") private val k9DriftGruppeId: String,
) {
@Bean
fun authorizationService() = AuthorizationService(ContextHolder.INSTANCE, k9DriftGruppeId)
}
39 changes: 39 additions & 0 deletions src/main/kotlin/no/nav/sifinnsynapi/sikkerhet/ContextHolder.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package no.nav.sifinnsynapi.sikkerhet

import no.nav.security.token.support.core.jwt.JwtToken
import no.nav.security.token.support.spring.SpringTokenValidationContextHolder
import org.springframework.web.context.request.RequestContextHolder

class ContextHolder private constructor(private val context: SpringTokenValidationContextHolder) {

companion object {
const val AZURE_AD = "azure"
private var instans: ContextHolder? = null
val INSTANCE: ContextHolder
get() {
if (instans == null) {
instans = ContextHolder(SpringTokenValidationContextHolder())
}
return instans!!
}

}

fun requestKontekst(): RequestKontekst? {
if (RequestContextHolder.getRequestAttributes() == null)
return null

val tokenContext = context.getTokenValidationContext()
val reqIssuerShortNames = tokenContext.issuers //alle issuers på alle validerte tokens i context
if (reqIssuerShortNames.contains(AZURE_AD)) {
val jwtToken: JwtToken? = tokenContext.getJwtToken(AZURE_AD)
return jwtToken?.let { RequestKontekst(it, AZURE_AD) }
}
return null
}

@JvmRecord
data class RequestKontekst(val jwtToken: JwtToken, val issuerShortname: String)


}
2 changes: 1 addition & 1 deletion src/main/kotlin/no/nav/sifinnsynapi/soknad/SøknadDAO.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import java.util.*
@Entity(name = "søknad")
data class SøknadDAO(
@Column(name = "id") @Id @JdbcTypeCode(SqlTypes.UUID) val id: UUID = UUID.randomUUID(),
@Column(name = "aktør_id") @Embedded val aktørId: AktørId,
@Column(name = "aktør_id") @Embedded internal val aktørId: AktørId,
@Column(name = "fødselsnummer") @Embedded valdselsnummer: Fødselsnummer,
@Column(name = "søknadstype") @Enumerated(STRING) valknadstype: Søknadstype,
@Column(name = "status") @Enumerated(STRING) val status: SøknadsStatus,
Expand Down
13 changes: 11 additions & 2 deletions src/main/kotlin/no/nav/sifinnsynapi/soknad/SøknadService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package no.nav.sifinnsynapi.soknad
import com.fasterxml.jackson.core.type.TypeReference
import com.fasterxml.jackson.databind.ObjectMapper
import no.nav.sifinnsynapi.common.AktørId
import no.nav.sifinnsynapi.common.Fødselsnummer
import no.nav.sifinnsynapi.common.Søknadstype
import no.nav.sifinnsynapi.dokument.DokumentDTO
import no.nav.sifinnsynapi.dokument.DokumentService
Expand Down Expand Up @@ -53,8 +54,9 @@ class SøknadService(

return søknadDAOs
.map { søknadDAO ->
val relevanteDokumenter = dokumentOversikt.filter { it.journalpostId == søknadDAO.journalpostId }
søknadDAO.tilSøknadDTO(relevanteDokumenter) }
val relevanteDokumenter = dokumentOversikt.filter { it.journalpostId == søknadDAO.journalpostId }
søknadDAO.tilSøknadDTO(relevanteDokumenter)
}
}

fun hentSøknad(søknadId: UUID): SøknadDTO {
Expand Down Expand Up @@ -121,6 +123,13 @@ class SøknadService(
fun finnUnikeSøknaderUtenMikrofrontendSisteSeksMåneder(søknadstype: Søknadstype, limit: Int): List<SøknadDAO> {
return repo.finnUnikeSøknaderUtenMikrofrontendSisteSeksMåneder(søknadstype.name, limit)
}

fun oppdaterAktørId(gyldigAktørId: AktørId, utgåttAktørId: AktørId): Int {
val søknader = repo.findAllByAktørId(utgåttAktørId);
søknader.map { it.copy( aktørId = gyldigAktørId ) }
.forEach { repo.save(it) }
return søknader.size;
}
}

class SøknadNotFoundException(søknadId: String) :
Expand Down
12 changes: 12 additions & 0 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,14 @@ no.nav:
saf-selvbetjening-base-url: # Settes i nais/<cluster>.json

security:
k9-drift-gruppe: ${K9_DRIFT_GRUPPE_ID}
cors:
allowed-origins: # Settes i nais/<cluster>.json
jwt:
issuer:
azure:
discoveryUrl: ${AZURE_APP_WELL_KNOWN_URL}
accepted_audience: ${AZURE_APP_CLIENT_ID}
tokenx:
discoveryUrl: ${TOKEN_X_WELL_KNOWN_URL}
accepted_audience: ${TOKEN_X_CLIENT_ID}
Expand Down Expand Up @@ -187,3 +191,11 @@ springdoc:
enabled: ${SWAGGER_ENABLED:false}
disable-swagger-default-url: true
path: swagger-ui.html
oauth:
use-pkce-with-authorization-code-grant: true
client-id: ${AZURE_APP_CLIENT_ID}
scope-separator: ","
oAuthFlow:
authorizationUrl: ${AZURE_LOGIN_URL:http://localhost:8080}/authorize
tokenUrl: ${AZURE_LOGIN_URL:http://localhost:8080}/token
apiScope: api://${AZURE_APP_CLIENT_ID:abc123}/.default
Loading

0 comments on commit 1dd2509

Please sign in to comment.