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 = "",