All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.http4k.security.CredentialsProvider.kt Maven / Gradle / Ivy

There is a newer version: 5.31.0.0
Show newest version
package org.http4k.security

import java.time.Clock
import java.time.Duration
import java.time.Duration.ZERO
import java.time.Instant
import java.util.concurrent.atomic.AtomicReference

fun interface CredentialsProvider : () -> T? {
    companion object
}

fun interface RefreshCredentials : (T?) -> ExpiringCredentials?

data class ExpiringCredentials(val credentials: T, val expiry: Instant)

fun  CredentialsProvider.Companion.Refreshing(
    gracePeriod: Duration = Duration.ofSeconds(10),
    clock: Clock = Clock.systemUTC(),
    refreshFn: RefreshCredentials
) = Refreshing(gracePeriod, clock::instant, refreshFn)

fun  CredentialsProvider.Companion.Refreshing(
    gracePeriod: Duration = Duration.ofSeconds(10),
    timeSource: () -> Instant,
    refreshFn: RefreshCredentials
) = object : CredentialsProvider {
    private val stored = AtomicReference>(null)

    private val isRefreshing = AtomicReference(false)

    override fun invoke() = (stored.get()
        ?.takeIf {
            val expiresSoon = it.expiresWithin(gracePeriod)
            val expiresReallySoon = it.expiresWithin(gracePeriod.dividedBy(2))
            val isAlreadyRefreshing = isRefreshing.get()

            (!expiresSoon || isAlreadyRefreshing) && !expiresReallySoon
        } ?: refresh())?.credentials

    private fun refresh() = synchronized(this) {
        isRefreshing.set(true)
        try {
            val current = stored.get()
            when {
                current != null && !current.expiresWithin(gracePeriod) -> current
                else -> try {
                    refreshFn(current?.credentials).also(stored::set)
                } catch (e: Exception) {
                    if (current == null || current.expiresWithin(ZERO)) throw e
                    current
                }
            }
        } finally {
            isRefreshing.set(false)
        }
    }

    private fun ExpiringCredentials.expiresWithin(duration: Duration): Boolean =
        expiry
            .minus(duration)
            .isBefore(timeSource())
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy