From 7d4d1283477ef9082d2d0b0a648102e127b325f3 Mon Sep 17 00:00:00 2001 From: Ingleiv Johansen Date: Tue, 18 Feb 2025 14:48:17 +0100 Subject: [PATCH] =?UTF-8?q?Caffeine-cache=20p=C3=A5=20AD-oppslag?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 10 ++++ .../no/nav/navansatt/ActiveDirectoryClient.kt | 46 ++++++++++++++++--- src/main/kotlin/no/nav/navansatt/Main.kt | 4 +- .../navansatt/ActiveDirectoryClientTest.kt | 2 +- 4 files changed, 53 insertions(+), 9 deletions(-) diff --git a/pom.xml b/pom.xml index aa71a2b..4ff0e29 100644 --- a/pom.xml +++ b/pom.xml @@ -114,6 +114,16 @@ io.ktor ktor-server-metrics-micrometer-jvm + + com.github.ben-manes.caffeine + caffeine + 3.2.0 + + + dev.hsbrysk + caffeine-coroutines + 2.0.0 + diff --git a/src/main/kotlin/no/nav/navansatt/ActiveDirectoryClient.kt b/src/main/kotlin/no/nav/navansatt/ActiveDirectoryClient.kt index 155e3a8..784055d 100644 --- a/src/main/kotlin/no/nav/navansatt/ActiveDirectoryClient.kt +++ b/src/main/kotlin/no/nav/navansatt/ActiveDirectoryClient.kt @@ -1,13 +1,16 @@ package no.nav.navansatt +import com.github.benmanes.caffeine.cache.* +import dev.hsbrysk.caffeine.CoroutineLoadingCache +import dev.hsbrysk.caffeine.buildCoroutine import io.opentelemetry.api.trace.SpanKind import io.opentelemetry.instrumentation.annotations.WithSpan import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import org.apache.commons.text.StringEscapeUtils import org.slf4j.LoggerFactory -import java.util.Hashtable -import java.util.Locale +import java.time.Duration +import java.util.* import java.util.regex.Pattern import javax.naming.Context import javax.naming.NamingEnumeration @@ -17,6 +20,7 @@ import javax.naming.directory.BasicAttributes import javax.naming.directory.SearchControls import javax.naming.ldap.InitialLdapContext + data class User( val ident: String, val displayName: String, @@ -26,12 +30,17 @@ data class User( val groups: List, ) -class ActiveDirectoryClient( +interface ActiveDirectoryClient { + suspend fun getUsers(idents: List): List + suspend fun getUser(ident: String): User? +} + +class DefaultActiveDirectoryClient( val url: String, val base: String, val username: String, val password: String?, -) { +): ActiveDirectoryClient { companion object { private val LOG = LoggerFactory.getLogger(ActiveDirectoryClient::class.java) } @@ -46,7 +55,7 @@ class ActiveDirectoryClient( } @WithSpan(kind = SpanKind.CLIENT) - suspend fun getUsers(idents: List): List = withContext(Dispatchers.IO) { + override suspend fun getUsers(idents: List): List = withContext(Dispatchers.IO) { val root = InitialLdapContext(env, null) val filter = (0..(idents.size - 1)).map { @@ -95,7 +104,7 @@ class ActiveDirectoryClient( } @WithSpan(kind = SpanKind.CLIENT) - suspend fun getUser(ident: String): User? = withContext(Dispatchers.IO) { + override suspend fun getUser(ident: String): User? = withContext(Dispatchers.IO) { val root = InitialLdapContext(env, null) val attrs = BasicAttributes().apply { @@ -180,3 +189,28 @@ class ActiveDirectoryClient( return null } } + +class CachedActiveDirectoryClient( + private val activeDirectoryClient: ActiveDirectoryClient +) : ActiveDirectoryClient { + + private val usersByIdents: CoroutineLoadingCache, List> = Caffeine.newBuilder() + .maximumSize(100) + .expireAfterWrite(Duration.ofMinutes(60)) + .refreshAfterWrite(Duration.ofMinutes(60)) + .buildCoroutine() { key -> activeDirectoryClient.getUsers(key) } + + private val userByIdent: CoroutineLoadingCache = Caffeine.newBuilder() + .maximumSize(1000) + .expireAfterWrite(Duration.ofMinutes(60)) + .refreshAfterWrite(Duration.ofMinutes(60)) + .buildCoroutine() { key -> activeDirectoryClient.getUser(key) } + + override suspend fun getUsers(idents: List): List { + return usersByIdents.get(idents.sorted()) + } + + override suspend fun getUser(ident: String): User? { + return userByIdent.get(ident) + } +} diff --git a/src/main/kotlin/no/nav/navansatt/Main.kt b/src/main/kotlin/no/nav/navansatt/Main.kt index 05dbc29..4e31cfe 100644 --- a/src/main/kotlin/no/nav/navansatt/Main.kt +++ b/src/main/kotlin/no/nav/navansatt/Main.kt @@ -17,12 +17,12 @@ data class ApiError( fun main() { val config = if (System.getenv("NAIS_APP_NAME") != null) appConfigNais() else appConfigLocal() - val activeDirectoryClient = ActiveDirectoryClient( + val activeDirectoryClient: ActiveDirectoryClient = CachedActiveDirectoryClient(DefaultActiveDirectoryClient( url = config.adUrl, base = config.adBase, username = config.adUsername, password = config.adPassword - ) + )) val httpClient = HttpClient(Apache5) { engine { sslContext = SSLContexts.createSystemDefault() diff --git a/src/test/kotlin/no/nav/navansatt/ActiveDirectoryClientTest.kt b/src/test/kotlin/no/nav/navansatt/ActiveDirectoryClientTest.kt index 6ccb535..217e202 100644 --- a/src/test/kotlin/no/nav/navansatt/ActiveDirectoryClientTest.kt +++ b/src/test/kotlin/no/nav/navansatt/ActiveDirectoryClientTest.kt @@ -30,7 +30,7 @@ class ActiveDirectoryClientTest { // Wait for the LDAP server to boot Thread.sleep(1500) - val activeDirectoryClient = ActiveDirectoryClient( + val activeDirectoryClient = DefaultActiveDirectoryClient( url = "ldap://localhost:$freePort", base = "DC=test,DC=local", username = "",