Skip to content
This repository was archived by the owner on Oct 8, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
ae28606
Introduces koin dependency injection
May 28, 2024
a0fceb2
Migrates database object to Koin singleton
May 28, 2024
6ad7ace
Bump jvm from 1.9.24 to 2.0.0
dependabot[bot] May 28, 2024
e9f954b
Bump org.assertj:assertj-core from 3.25.3 to 3.26.0
dependabot[bot] May 29, 2024
f7f6d60
Bump io.ktor.plugin from 2.3.10 to 2.3.11
dependabot[bot] May 30, 2024
853f60d
Bump exposedVersion from 0.50.1 to 0.51.0
dependabot[bot] May 31, 2024
aee6167
[AAP-18] Legger til uthenting av NAVident fra jwt token
Jun 3, 2024
2813bf4
Gir 204 for ok kall til frigi og tildel
steoiv Jun 3, 2024
62a0351
[AAP-18] Legger til logging på authfilter for å hente testbrukerident…
Jun 3, 2024
4659ab5
[AAP-18] Legger til logging av token i securelogs.. for debugging
Jun 3, 2024
3cc9225
[AAP-18] Legger til tilgangsstyringsmock
Jun 3, 2024
580d6c7
[AAP-18] Legger til "Tilgangstyring" på hent alle opgpaver
Jun 3, 2024
af2434f
[AAP-18] Replaces NavIdend with group claim
Jun 3, 2024
8eb646b
[AAP-18] Adds logging for debuging token
Jun 3, 2024
cb1547c
Bump org.flywaydb:flyway-database-postgresql from 10.13.0 to 10.14.0
dependabot[bot] Jun 3, 2024
b6dc3bb
Legger til grupper i principal
Jun 3, 2024
0cf256d
[AAP-7] Legger til mulighet for lagring og henting av filtere
Jun 4, 2024
0fd3b3d
Fjener autentisering for behandlingsflytadapter
Jun 4, 2024
f05b402
[AAP-18] Legger til alle roller for alle brukere intil videre
Jun 4, 2024
7c8755e
[AAP-119] Legger til filtrering og sortering for personnummer og tild…
Jun 4, 2024
53f598b
Bump org.flywaydb:flyway-core from 10.13.0 to 10.14.0
dependabot[bot] Jun 4, 2024
47d16de
[AAP-18] Legger til gruppestyring fra AD
Jun 5, 2024
5e6eade
Bump exposedVersion from 0.51.0 to 0.51.1
dependabot[bot] Jun 5, 2024
bbc9034
[AAP-22] Legger til endepunkt for behandling av neste sak
Jun 7, 2024
4193604
[AAP-7] Legger til sletting av filter
Jun 7, 2024
c0c5fbe
[AAP-7] Legger til id i OppgavefilterDto
Jun 7, 2024
1bb9376
Refaktorering av prosjektstrukturen
Jun 7, 2024
a02b6ef
Introduserer Koin dependency injection for Kotlin
Jun 10, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .nais/app-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ spec:
application:
enabled: true
allowAllUsers: true
claims:
extra:
- NAVident
groups:
- id: "8bb0ee13-49cd-4e75-8c3d-a13420c8b376"
- id: "12353679-aa80-4e59-bb47-95e727bfe85c"
- id: "b60d74dd-fcf7-4c53-a50b-7b20f51804a1"
resources:
limits:
memory: 256Mi
Expand Down
19 changes: 11 additions & 8 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar

plugins {
kotlin("jvm") version "1.9.24"
id("io.ktor.plugin") version "2.3.10"
kotlin("jvm") version "2.0.0"
id("io.ktor.plugin") version "2.3.11"
}

val aapLibVersion = "5.0.15"
val ktorVersion = "2.3.11"
val exposedVersion = "0.50.1"
val exposedVersion = "0.51.1"

application {
mainClass.set("oppgavestyring.AppKt")
}

dependencies {
implementation("com.github.navikt.aap-libs:ktor-auth:$aapLibVersion")
implementation("io.ktor:ktor-server-auth:$ktorVersion")
implementation("io.ktor:ktor-server-auth-jwt:$ktorVersion")
implementation("io.ktor:ktor-server-core:$ktorVersion")
implementation("io.ktor:ktor-server-netty:$ktorVersion")
implementation("io.ktor:ktor-server-metrics-micrometer:$ktorVersion")
Expand All @@ -28,15 +30,16 @@ dependencies {
implementation("io.ktor:ktor-client-core:$ktorVersion")
implementation("io.ktor:ktor-client-logging:$ktorVersion")

implementation(kotlin("reflect"))
// Koin for Kotlin apps
implementation("io.insert-koin:koin-ktor:3.5.6")

// persistence
implementation("org.jetbrains.exposed:exposed-core:$exposedVersion")
implementation("org.jetbrains.exposed:exposed-jdbc:$exposedVersion")
implementation("org.jetbrains.exposed:exposed-dao:$exposedVersion")
implementation("org.jetbrains.exposed:exposed-java-time:$exposedVersion")
implementation("org.flywaydb:flyway-core:10.13.0")
implementation("org.flywaydb:flyway-database-postgresql:10.13.0")
implementation("org.flywaydb:flyway-core:10.14.0")
implementation("org.flywaydb:flyway-database-postgresql:10.14.0")
implementation("com.zaxxer:HikariCP:5.1.0")
runtimeOnly("org.postgresql:postgresql:42.7.3")
testImplementation("org.testcontainers:postgresql:1.19.8")
Expand All @@ -49,9 +52,9 @@ dependencies {

testImplementation(kotlin("test"))
testImplementation("io.ktor:ktor-server-test-host:$ktorVersion")
testImplementation("org.assertj:assertj-core:3.25.3")
testImplementation("org.assertj:assertj-core:3.26.0")
testImplementation("io.mockk:mockk:1.13.10")
testImplementation("org.assertj:assertj-core:3.25.3")
testImplementation("org.assertj:assertj-core:3.26.0")
}

repositories {
Expand Down
60 changes: 33 additions & 27 deletions app/src/main/kotlin/oppgavestyring/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
import io.ktor.http.*
import io.ktor.serialization.jackson.*
import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.server.engine.*
import io.ktor.server.metrics.micrometer.*
import io.ktor.server.netty.*
Expand All @@ -18,13 +19,13 @@ import io.micrometer.core.instrument.binder.logging.LogbackMetrics
import io.micrometer.prometheus.PrometheusConfig
import io.micrometer.prometheus.PrometheusMeterRegistry
import oppgavestyring.actuators.api.actuators
import oppgavestyring.behandlingsflyt.BehandlingsflytAdapter
import oppgavestyring.behandlingsflyt.behandlingsflyt
import oppgavestyring.config.db.DatabaseSingleton
import oppgavestyring.config.db.DbConfig
import oppgavestyring.oppgave.OppgaveService
import oppgavestyring.oppgave.adapter.Token
import oppgavestyring.oppgave.api.oppgaver
import oppgavestyring.config.koinModule
import oppgavestyring.config.security.AZURE
import oppgavestyring.config.security.authentication
import oppgavestyring.ekstern.behandlingsflyt.behandlingsflyt
import oppgavestyring.intern.filter.filter
import oppgavestyring.intern.oppgave.api.oppgaver
import org.koin.ktor.plugin.Koin
import org.slf4j.Logger
import org.slf4j.LoggerFactory

Expand All @@ -36,14 +37,10 @@ fun main() {
embeddedServer(Netty, port = 8080, module = Application::server).start(wait = true)
}

fun Application.server(
config: Config = Config(),
) {
val prometheus = PrometheusMeterRegistry(PrometheusConfig.DEFAULT)
fun Application.oppgavestyring(config: Config) {

install(MicrometerMetrics) {
registry = prometheus
meterBinders += LogbackMetrics()
install(Koin) {
modules(koinModule)
}

install(ContentNegotiation) {
Expand All @@ -67,22 +64,31 @@ fun Application.server(
}
}

DatabaseSingleton.init(DbConfig())
DatabaseSingleton.migrate()

val oppgaveService = OppgaveService()
val behandlingsflytAdapter = BehandlingsflytAdapter(oppgaveService)
authentication(config.azure)

routing {
actuators(prometheus)
oppgaver(oppgaveService)
behandlingsflyt(behandlingsflytAdapter)
authenticate(AZURE) {
oppgaver()
filter()
}
behandlingsflyt()

}
}

internal fun ApplicationCall.authToken(): Token? {
return request.headers["Authorization"]
?.split(" ")
?.get(1)
?.let { Token(it) }
fun Application.server(
config: Config = Config(),
) {
val prometheus = PrometheusMeterRegistry(PrometheusConfig.DEFAULT)

install(MicrometerMetrics) {
registry = prometheus
meterBinders += LogbackMetrics()
}

oppgavestyring(config)

routing {
actuators()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ import io.ktor.server.application.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import io.micrometer.prometheus.PrometheusMeterRegistry
import org.koin.ktor.ext.inject

fun Route.actuators(prometheus: PrometheusMeterRegistry) {
fun Route.actuators() {

val prometheus: PrometheusMeterRegistry by inject()

route("/actuator") {
get("/live") {
Expand Down
18 changes: 18 additions & 0 deletions app/src/main/kotlin/oppgavestyring/config/Koin.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package oppgavestyring.config

import io.micrometer.prometheus.PrometheusConfig
import io.micrometer.prometheus.PrometheusMeterRegistry
import oppgavestyring.config.db.DatabaseConfiguration
import oppgavestyring.config.db.DatabaseManager
import oppgavestyring.ekstern.behandlingsflyt.BehandlingsflytAdapter
import oppgavestyring.intern.oppgave.OppgaveService
import org.koin.dsl.module

val koinModule = module {

single { OppgaveService() }
single { BehandlingsflytAdapter(get()) }
single { PrometheusMeterRegistry(PrometheusConfig.DEFAULT) }
single(createdAtStart = true) { DatabaseManager(DatabaseConfiguration()) }

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package oppgavestyring.config.db

val DB_CONFIG_PREFIX = "DB_OPPGAVESTYRING"

data class DbConfig(
data class DatabaseConfiguration(
val connectionURL: String = System.getenv("${DB_CONFIG_PREFIX}_JDBC_URL") ?:
System.getProperty("${DB_CONFIG_PREFIX}_JDBC_URL"),
val username: String = System.getenv("${DB_CONFIG_PREFIX}_USERNAME") ?:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,25 @@ import oppgavestyring.LOG
import org.jetbrains.exposed.sql.Database
import javax.sql.DataSource

object DatabaseSingleton {
class DatabaseManager(databaseConfig: DatabaseConfiguration) {

var connection: DataSource? = null
val connection: DataSource

fun init(config: DbConfig) {
init {
LOG.info("Setting up database connection")
if (connection != null) return

connection = createHikariDataSource(config)
Database.connect(connection!!)
connection = createHikariDataSource(databaseConfig)
Database.connect(connection)
LOG.info("Database connection established")
migrate()
}

fun migrate() {
LOG.info("Migrating database")
require(connection != null) {"Database connection is required"}
connection?.let { Flyway.migrate(it) }
Flyway.migrate(connection)
LOG.info("Database connection completed")
}

private fun createHikariDataSource(dbConfig: DbConfig) = HikariDataSource(HikariConfig().apply {
private fun createHikariDataSource(dbConfig: DatabaseConfiguration) = HikariDataSource(HikariConfig().apply {
driverClassName = dbConfig.driver
jdbcUrl = dbConfig.connectionURL
username = dbConfig.username
Expand Down
14 changes: 14 additions & 0 deletions app/src/main/kotlin/oppgavestyring/config/security/AdGruppe.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package oppgavestyring.config.security

import java.util.*

enum class AdGruppe(private val gruppeId: UUID) {

SAKSBEHANDLER(UUID.fromString("8bb0ee13-49cd-4e75-8c3d-a13420c8b376")),
VEILEDER(UUID.fromString("12353679-aa80-4e59-bb47-95e727bfe85c")),
AVDELINGSLEDER(UUID.fromString("f0f6cad5-e3c0-4308-99a2-3630ac60174a"));

companion object {
fun valueOf(gruppeId: UUID) = entries.find { gruppeId == it.gruppeId }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package oppgavestyring.config.security

import com.auth0.jwk.JwkProviderBuilder
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.server.auth.jwt.*
import io.ktor.server.response.*
import no.nav.aap.ktor.client.auth.azure.AzureConfig
import oppgavestyring.SECURE_LOG
import java.net.URI
import java.util.*
import java.util.concurrent.TimeUnit

val NAV_IDENT_CLAIM_NAME = "NAVident"

const val AZURE = "azure"
fun Application.authentication(config: AzureConfig) {

val jwkProvider = JwkProviderBuilder(URI.create(config.jwksUri).toURL()).cached(10, 24, TimeUnit.HOURS)
.rateLimited(10, 1, TimeUnit.MINUTES).build()

authentication {
jwt(AZURE) {
verifier(jwkProvider, config.issuer)
challenge { _, _ ->
call.respond(HttpStatusCode.Unauthorized, "AzureAD validering feilet") }
validate { cred ->
val now = Date()
SECURE_LOG.info("Ident of requester: ${cred.getClaim(NAV_IDENT_CLAIM_NAME, String::class)}")
SECURE_LOG.info("Groups of requester: ${cred.getListClaim("groups", String::class)}")
if (config.clientId !in cred.audience) {
SECURE_LOG.warn("AzureAD validering feilet (clientId var ikke i audience: ${cred.audience}")
return@validate null
}

if (cred.expiresAt?.before(now) == true) {
SECURE_LOG.warn("AzureAD validering feilet (expired at: ${cred.expiresAt})")
return@validate null
}

if (cred.notBefore?.after(now) == true) {
SECURE_LOG.warn("AzureAD validering feilet (not valid yet, valid from: ${cred.notBefore})")
return@validate null
}

if (cred.issuedAt?.after(cred.expiresAt ?: return@validate null) == true) {
SECURE_LOG.warn("AzureAD validering feilet (issued after expiration: ${cred.issuedAt} )")
return@validate null
}

OppgavePrincipal.fromJwt(cred)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package oppgavestyring.config.security

import io.ktor.server.auth.*
import io.ktor.server.auth.jwt.*
import oppgavestyring.intern.oppgave.NavIdent
import java.util.*

data class OppgavePrincipal(
val ident: NavIdent,
val grupper: List<AdGruppe>
): Principal {
companion object {
fun fromJwt(jwt: JWTCredential) = OppgavePrincipal(
NavIdent(jwt.getClaim(NAV_IDENT_CLAIM_NAME, String::class) ?: throw IllegalArgumentException("JWT mangler Ident")),
jwt.getListClaim("groups", UUID::class).map { AdGruppe.valueOf(it) }.filterNotNull()
)
}

fun isVeileder() = AdGruppe.VEILEDER in grupper || AdGruppe.AVDELINGSLEDER in grupper
fun isSaksbehandler() = AdGruppe.SAKSBEHANDLER in grupper || AdGruppe.AVDELINGSLEDER in grupper
fun isAvdelingsleder() = AdGruppe.AVDELINGSLEDER in grupper
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package oppgavestyring.behandlingsflyt
package oppgavestyring.ekstern.behandlingsflyt

import oppgavestyring.behandlingsflyt.dto.Avklaringsbehovtype
import oppgavestyring.behandlingsflyt.dto.BehandlingshistorikkRequest
import oppgavestyring.oppgave.OppgaveService
import oppgavestyring.ekstern.behandlingsflyt.dto.Avklaringsbehovtype
import oppgavestyring.ekstern.behandlingsflyt.dto.BehandlingshistorikkRequest
import oppgavestyring.intern.oppgave.OppgaveService


class BehandlingsflytAdapter(
Expand All @@ -11,7 +11,8 @@ class BehandlingsflytAdapter(


fun mapBehnadlingshistorikkTilOppgaveHendelser(
behanlding: BehandlingshistorikkRequest) {
behanlding: BehandlingshistorikkRequest
) {

oppgaveService.lukkOppgaverPåBehandling(behanlding.referanse)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
package oppgavestyring.behandlingsflyt
package oppgavestyring.ekstern.behandlingsflyt

import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import oppgavestyring.LOG
import oppgavestyring.behandlingsflyt.dto.BehandlingshistorikkRequest
import oppgavestyring.ekstern.behandlingsflyt.dto.BehandlingshistorikkRequest
import org.jetbrains.exposed.sql.transactions.transaction
import org.koin.ktor.ext.inject

fun Route.behandlingsflyt() {

val behandlingsflytAdapter: BehandlingsflytAdapter by inject()

fun Route.behandlingsflyt(behandlingsflytAdapter: BehandlingsflytAdapter) {
route("/behandling") {
post {
LOG.info("Mottok saksendring på behandling")
Expand Down
Loading