Skip to content

Commit

Permalink
Add kontaktinfo API
Browse files Browse the repository at this point in the history
  • Loading branch information
AudunSorheim committed Dec 15, 2023
1 parent b69efe6 commit 6895b14
Show file tree
Hide file tree
Showing 40 changed files with 1,137 additions and 7 deletions.
28 changes: 28 additions & 0 deletions .github/workflows/deploy-redis.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Deploy Redis
on:
push:
paths: ['nais/redis-dev.yaml', 'nais/redis-prod.yaml']
workflow_dispatch:

jobs:
deploy-redis-dev:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: nais/deploy/actions/deploy@v1
env:
APIKEY: ${{ secrets.NAIS_DEPLOY_APIKEY }}
CLUSTER: dev-gcp
RESOURCE: nais/redis-dev.yaml

deploy-redis-prod:
if: github.ref == 'refs/heads/main'
needs: deploy-redis-dev
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: nais/deploy/actions/deploy@v1
env:
APIKEY: ${{ secrets.NAIS_DEPLOY_APIKEY }}
CLUSTER: prod-gcp
RESOURCE: nais/redis-prod.yaml
6 changes: 3 additions & 3 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,13 @@ val logstashLogbackEncoderVersion = "7.4"
dependencies {
implementation("org.springframework.boot:spring-boot-starter")
implementation("org.springframework.boot:spring-boot-starter-jdbc")
implementation("org.springframework.boot:spring-boot-starter-oauth2-client")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("org.springframework.boot:spring-boot-starter-cache")
implementation("org.springframework.boot:spring-boot-starter-data-redis")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("io.micrometer:micrometer-registry-prometheus")
implementation("org.hibernate.validator:hibernate-validator")
implementation("org.postgresql:postgresql")
implementation("org.flywaydb:flyway-core")
implementation("org.jetbrains.kotlin:kotlin-reflect")
Expand All @@ -54,7 +55,6 @@ dependencies {
developmentOnly("org.springframework.boot:spring-boot-devtools")
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.springframework.security:spring-security-test")
testImplementation("io.mockk:mockk:$mockkVersion")
testImplementation("io.kotest:kotest-runner-junit5-jvm:$kotestVersion")
testImplementation("io.kotest:kotest-assertions-core:$kotestVersion")
Expand Down
1 change: 1 addition & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
org.gradle.jvmargs=-Xmx2048M -Dkotlin.daemon.jvm.options="-Xmx2048M" -Dfile.encoding=UTF-8
36 changes: 36 additions & 0 deletions nais/nais-dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,28 @@ spec:
pool: nav-dev
tokenx:
enabled: true
accessPolicy:
inbound:
rules:
- application: oppfolgingsplan-frontend
outbound:
rules:
- application: digdir-krr-proxy
namespace: team-rocket
cluster: dev-gcp
- application: syfobrukertilgang
namespace: team-esyfo
cluster: dev-gcp
azure:
application:
allowAllUsers: true
enabled: true
tenant: trygdeetaten.no
replyURLs:
- "https://oppfolgingsplan-backend.intern.dev.nav.no/oauth2/callback"
claims:
extra:
- "NAVident"
gcp:
sqlInstances:
- type: POSTGRES_14
Expand All @@ -59,3 +81,17 @@ spec:
flags:
- name: cloudsql.logical_decoding
value: "on"
env:
- name: KRR_URL
value: https://digdir-krr-proxy.intern.dev.nav.no/rest/v1/person
- name: KRR_SCOPE
value: dev-gcp.team-rocket.digdir-krr-proxy
- name: SYFOBRUKERTILGANG_URL
value: http://syfobrukertilgang
- name: SYFOBRUKERTILGANG_ID
value: dev-gcp:team-esyfo:syfobrukertilgang
- name: OPPFOLGINGSPLAN_FRONTEND_CLIENT_ID
value: dev-gcp:team-esyfo:oppfolgingsplan-frontend
redis:
- instance: oppfolgingsplan
access: readwrite
36 changes: 36 additions & 0 deletions nais/nais-prod.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,28 @@ spec:
pool: nav-prod
tokenx:
enabled: true
accessPolicy:
inbound:
rules:
- application: oppfolgingsplan-frontend
outbound:
rules:
- application: digdir-krr-proxy
namespace: team-rocket
cluster: prod-gcp
- application: syfobrukertilgang
namespace: team-esyfo
cluster: prod-gcp
azure:
application:
allowAllUsers: true
enabled: true
tenant: nav.no
replyURLs:
- "https://oppfolgingsplan-backend.intern.nav.no/oauth2/callback"
claims:
extra:
- "NAVident"
gcp:
sqlInstances:
- type: POSTGRES_14
Expand All @@ -59,3 +81,17 @@ spec:
flags:
- name: cloudsql.logical_decoding
value: "on"
env:
- name: KRR_URL
value: https://digdir-krr-proxy.intern.nav.no/rest/v1/person
- name: KRR_SCOPE
value: prod-gcp.team-rocket.digdir-krr-proxy
- name: SYFOBRUKERTILGANG_URL
value: http://syfobrukertilgang
- name: SYFOBRUKERTILGANG_ID
value: prod-gcp:team-esyfo:syfobrukertilgang
- name: OPPFOLGINGSPLAN_FRONTEND_CLIENT_ID
value: prod-gcp:team-esyfo:oppfolgingsplan-frontend
redis:
- instance: oppfolgingsplan
access: readwrite
11 changes: 11 additions & 0 deletions nais/redis-dev.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
apiVersion: aiven.io/v1alpha1
kind: Redis
metadata:
labels:
app: oppfolgingsplan-redis
team: team-esyfo
name: redis-team-esyfo-oppfolgingsplan
namespace: team-esyfo
spec:
plan: startup-4
project: nav-dev
11 changes: 11 additions & 0 deletions nais/redis-prod.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
apiVersion: aiven.io/v1alpha1
kind: Redis
metadata:
labels:
app: oppfolgingsplan-redis
team: team-esyfo
name: redis-team-esyfo-oppfolgingsplan
namespace: team-esyfo
spec:
plan: startup-4
project: nav-prod
8 changes: 8 additions & 0 deletions src/main/kotlin/no/nav/syfo/Log.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package no.nav.syfo

import org.slf4j.Logger
import org.slf4j.LoggerFactory

inline fun <reified T> T.logger(): Logger {
return LoggerFactory.getLogger(T::class.java)
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package no.nav.syfo.oppfolgingsplanbackend
package no.nav.syfo

import no.nav.security.token.support.spring.api.EnableJwtTokenValidation
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication

@SpringBootApplication
@EnableJwtTokenValidation
class OppfolgingsplanBackendApplication

fun main(args: Array<String>) {
Expand Down
10 changes: 10 additions & 0 deletions src/main/kotlin/no/nav/syfo/auth/azure/AzureAdToken.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package no.nav.syfo.auth.azure

import java.io.Serializable
import java.time.LocalDateTime

@SuppressWarnings("SerialVersionUIDInSerializableClass")
data class AzureAdToken(
val accessToken: String,
val expires: LocalDateTime
) : Serializable
62 changes: 62 additions & 0 deletions src/main/kotlin/no/nav/syfo/auth/azure/AzureAdTokenClient.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package no.nav.syfo.auth.azure

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
import org.springframework.http.HttpEntity
import org.springframework.http.HttpHeaders
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import org.springframework.stereotype.Component
import org.springframework.util.LinkedMultiValueMap
import org.springframework.util.MultiValueMap
import org.springframework.web.client.RestClientResponseException
import org.springframework.web.client.RestTemplate
import java.nio.charset.Charset

@Component
class AzureAdTokenClient @Autowired constructor(
@Value("\${azure.app.client.id}") private val azureAppClientId: String,
@Value("\${azure.app.client.secret}") private val azureAppClientSecret: String,
@Value("\${azure.openid.config.token.endpoint}") private val azureTokenEndpoint: String
) {
fun getOnBehalfOfToken(scopeClientId: String, token: String): String {
return getToken(requestEntity(scopeClientId, token))
}

fun getSystemToken(scopeClientId: String): String {
return getToken(systemTokenRequestEntity(scopeClientId))
}

private fun getToken(requestEntity: HttpEntity<MultiValueMap<String, String>>): String {
val response = RestTemplate().postForEntity(azureTokenEndpoint, requestEntity, AzureAdTokenResponse::class.java)
return response.body?.toAzureAdToken()?.accessToken ?: throw RestClientResponseException(
"Failed to get token",
HttpStatus.INTERNAL_SERVER_ERROR.value(),
"",
HttpHeaders(),
ByteArray(0),
Charset.defaultCharset()
)
}

private fun requestEntity(scopeClientId: String, token: String): HttpEntity<MultiValueMap<String, String>> {
val body = LinkedMultiValueMap<String, String>()
body.add("client_id", azureAppClientId)
body.add("client_secret", azureAppClientSecret)
body.add("client_assertion_type", "urn:ietf:params:oauth:grant-type:jwt-bearer")
body.add("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer")
body.add("assertion", token)
body.add("scope", "api://$scopeClientId/.default")
body.add("requested_token_use", "on_behalf_of")
return HttpEntity(body, HttpHeaders().apply { contentType = MediaType.MULTIPART_FORM_DATA })
}

private fun systemTokenRequestEntity(scopeClientId: String): HttpEntity<MultiValueMap<String, String>> {
val body = LinkedMultiValueMap<String, String>()
body.add("client_id", azureAppClientId)
body.add("scope", "api://$scopeClientId/.default")
body.add("grant_type", "client_credentials")
body.add("client_secret", azureAppClientSecret)
return HttpEntity(body, HttpHeaders().apply { contentType = MediaType.MULTIPART_FORM_DATA })
}
}
20 changes: 20 additions & 0 deletions src/main/kotlin/no/nav/syfo/auth/azure/AzureAdTokenResponse.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package no.nav.syfo.auth.azure

import com.fasterxml.jackson.annotation.JsonIgnoreProperties
import java.io.Serializable
import java.time.LocalDateTime

@SuppressWarnings("SerialVersionUIDInSerializableClass", "ConstructorParameterNaming")
@JsonIgnoreProperties(ignoreUnknown = true)
data class AzureAdTokenResponse(
val access_token: String,
val expires_in: Long
) : Serializable

fun AzureAdTokenResponse.toAzureAdToken(): AzureAdToken {
val expiresOn = LocalDateTime.now().plusSeconds(this.expires_in)
return AzureAdToken(
accessToken = this.access_token,
expires = expiresOn
)
}
5 changes: 5 additions & 0 deletions src/main/kotlin/no/nav/syfo/auth/oidc/OIDCIssuer.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package no.nav.syfo.auth.oidc

object OIDCIssuer {
const val INTERN_AZUREAD_V2 = "internazureadv2"
}
15 changes: 15 additions & 0 deletions src/main/kotlin/no/nav/syfo/auth/oidc/TokenUtil.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package no.nav.syfo.auth.oidc

import no.nav.security.token.support.core.context.TokenValidationContextHolder

object TokenUtil {

@JvmStatic
fun getIssuerToken(contextHolder: TokenValidationContextHolder, issuer: String): String {
val context = contextHolder.tokenValidationContext
return context.getJwtToken(issuer)?.tokenAsString
?: throw TokenValidationException("Klarte ikke hente token fra issuer: $issuer")
}

class TokenValidationException(message: String) : RuntimeException(message)
}
22 changes: 22 additions & 0 deletions src/main/kotlin/no/nav/syfo/auth/tokenx/TokenXResponse.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package no.nav.syfo.auth.tokenx

import com.fasterxml.jackson.annotation.JsonIgnoreProperties
import java.io.Serializable
import java.time.LocalDateTime

@SuppressWarnings("SerialVersionUIDInSerializableClass", "ConstructorParameterNaming")
@JsonIgnoreProperties(ignoreUnknown = true)
data class TokenXResponse(
val access_token: String,
val issued_token_type: String,
val token_type: String,
val expires_in: Long
) : Serializable

fun TokenXResponse.toTokenXToken(): TokenXToken {
val expiresOn = LocalDateTime.now().plusSeconds(this.expires_in)
return TokenXToken(
accessToken = this.access_token,
expires = expiresOn
)
}
10 changes: 10 additions & 0 deletions src/main/kotlin/no/nav/syfo/auth/tokenx/TokenXToken.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package no.nav.syfo.auth.tokenx

import java.io.Serializable
import java.time.LocalDateTime

@SuppressWarnings("SerialVersionUIDInSerializableClass")
data class TokenXToken(
val accessToken: String,
val expires: LocalDateTime
) : Serializable
38 changes: 38 additions & 0 deletions src/main/kotlin/no/nav/syfo/auth/tokenx/TokenXUtil.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package no.nav.syfo.auth.tokenx

import no.nav.security.token.support.core.context.TokenValidationContextHolder
import no.nav.security.token.support.core.jwt.JwtTokenClaims
import no.nav.syfo.domain.Fodselsnummer
import org.springframework.http.HttpStatus
import org.springframework.web.server.ResponseStatusException

object TokenXUtil {
@Throws(ResponseStatusException::class)
fun validateTokenXClaims(
contextHolder: TokenValidationContextHolder,
vararg requestedClientId: String,
): JwtTokenClaims {
val context = contextHolder.tokenValidationContext
val claims = context.getClaims(TokenXIssuer.TOKENX)
val clientId = claims.getStringClaim("client_id")

if (!requestedClientId.toList().contains(clientId)) {
throw ResponseStatusException(HttpStatus.FORBIDDEN, "Uventet client id $clientId")
}
return claims
}

fun JwtTokenClaims.fnrFromIdportenTokenX(): Fodselsnummer {
return Fodselsnummer(this.getStringClaim("pid"))
}

fun fnrFromIdportenTokenX(contextHolder: TokenValidationContextHolder): Fodselsnummer {
val context = contextHolder.tokenValidationContext
val claims = context.getClaims(TokenXIssuer.TOKENX)
return Fodselsnummer(claims.getStringClaim("pid"))
}

object TokenXIssuer {
const val TOKENX = "tokenx"
}
}
Loading

0 comments on commit 6895b14

Please sign in to comment.