From 651a7c730ec413d3cc9960f5ff75f6925b137f0a Mon Sep 17 00:00:00 2001 From: mrflick72 Date: Thu, 31 Oct 2024 17:02:23 +0100 Subject: [PATCH 1/8] first commit --- .../vauthenticator/server/job/DatabaseTtlEntryCleanJob.kt | 6 ++++++ .../adapter/jdbc/JdbcClientApplicationRepository.kt | 4 ++-- .../server/job/DatabaseTtlEntryCleanJobTest.kt | 8 ++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 src/main/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJob.kt create mode 100644 src/test/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJobTest.kt diff --git a/src/main/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJob.kt b/src/main/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJob.kt new file mode 100644 index 00000000..dd14c04d --- /dev/null +++ b/src/main/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJob.kt @@ -0,0 +1,6 @@ +package com.vauthenticator.server.job + +class DatabaseTtlEntryCleanJob { + + +} \ No newline at end of file diff --git a/src/main/kotlin/com/vauthenticator/server/oauth2/clientapp/adapter/jdbc/JdbcClientApplicationRepository.kt b/src/main/kotlin/com/vauthenticator/server/oauth2/clientapp/adapter/jdbc/JdbcClientApplicationRepository.kt index c7463c9a..837aeb5f 100644 --- a/src/main/kotlin/com/vauthenticator/server/oauth2/clientapp/adapter/jdbc/JdbcClientApplicationRepository.kt +++ b/src/main/kotlin/com/vauthenticator/server/oauth2/clientapp/adapter/jdbc/JdbcClientApplicationRepository.kt @@ -126,10 +126,10 @@ object JdbcClientApplicationConverter { webServerRedirectUri = CallbackUri(rs.getString("web_server_redirect_uri")), accessTokenValidity = TokenTimeToLive(rs.getLong("access_token_validity")), refreshTokenValidity = TokenTimeToLive(rs.getLong("refresh_token_validity")), - additionalInformation = objectMapper.readValue( + additionalInformation = Optional.ofNullable(objectMapper.readValue( rs.getString("additional_information"), Map::class.java - ) as Map, + ) as Map).orElse(emptyMap()), autoApprove = AutoApprove(rs.getBoolean("auto_approve")), postLogoutRedirectUri = PostLogoutRedirectUri(rs.getString("post_logout_redirect_uri")), logoutUri = LogoutUri(rs.getString("logout_uri")) diff --git a/src/test/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJobTest.kt b/src/test/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJobTest.kt new file mode 100644 index 00000000..5a76a0e8 --- /dev/null +++ b/src/test/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJobTest.kt @@ -0,0 +1,8 @@ +package com.vauthenticator.server.job + +import org.junit.jupiter.api.Assertions.* + +class DatabaseTtlEntryCleanJobTest { + + +} \ No newline at end of file From a544c409e2b678881c7578206d38faa06d2bd7a9 Mon Sep 17 00:00:00 2001 From: mrflick72 Date: Wed, 20 Nov 2024 22:02:24 +0100 Subject: [PATCH 2/8] revert useless class --- .../vauthenticator/server/job/DatabaseTtlEntryCleanJob.kt | 6 ------ .../server/job/DatabaseTtlEntryCleanJobTest.kt | 8 -------- 2 files changed, 14 deletions(-) delete mode 100644 src/main/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJob.kt delete mode 100644 src/test/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJobTest.kt diff --git a/src/main/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJob.kt b/src/main/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJob.kt deleted file mode 100644 index dd14c04d..00000000 --- a/src/main/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJob.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.vauthenticator.server.job - -class DatabaseTtlEntryCleanJob { - - -} \ No newline at end of file diff --git a/src/test/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJobTest.kt b/src/test/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJobTest.kt deleted file mode 100644 index 5a76a0e8..00000000 --- a/src/test/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJobTest.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.vauthenticator.server.job - -import org.junit.jupiter.api.Assertions.* - -class DatabaseTtlEntryCleanJobTest { - - -} \ No newline at end of file From c64db178a80e0ca47c44f11f023eee6ac4e2c3ea Mon Sep 17 00:00:00 2001 From: mrflick72 Date: Wed, 20 Nov 2024 23:04:53 +0100 Subject: [PATCH 3/8] now we are able to clean up old keys and ticket from the database --- .../server/job/DatabaseTtlEntryCleanJob.kt | 45 ++++++++++++++ .../job/DatabaseTtlEntryCleanJobTest.kt | 58 +++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 src/main/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJob.kt create mode 100644 src/test/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJobTest.kt diff --git a/src/main/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJob.kt b/src/main/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJob.kt new file mode 100644 index 00000000..59e447d2 --- /dev/null +++ b/src/main/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJob.kt @@ -0,0 +1,45 @@ +package com.vauthenticator.server.job + +import org.springframework.jdbc.core.JdbcTemplate +import org.springframework.jdbc.core.queryForList +import org.springframework.scheduling.annotation.Scheduled +import org.springframework.transaction.annotation.Transactional +import java.time.Clock + + +@Transactional +class DatabaseTtlEntryCleanJob( + private val jdbcTemplate: JdbcTemplate, + private val clock: Clock +) { + + @Scheduled(cron = "\${scheduled.database-cleanup.cron}") + fun execute() { + val now = clock.instant().epochSecond + + deleteOldTicket(now) + deleteOldKeys(now) + } + + private fun deleteOldKeys(now: Long) { + val keysToBeDeleted = + jdbcTemplate.queryForList("SELECT key_id,key_purpose FROM KEYS WHERE key_expiration_date_timestamp < ?", now) + keysToBeDeleted.forEach { + jdbcTemplate.update( + "DELETE FROM KEYS WHERE key_id = ? AND key_purpose = ?;", it["key_id"], it["key_purpose"] + ) + } + } + + private fun deleteOldTicket(now: Long) { + val ticketToBeDeleted = + jdbcTemplate.queryForList( + "SELECT ticket FROM TICKET WHERE ttl < ?", + arrayOf(now) + ) + ticketToBeDeleted.forEach { + jdbcTemplate.update("DELETE FROM TICKET WHERE ticket = ?", it) + } + } + +} \ No newline at end of file diff --git a/src/test/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJobTest.kt b/src/test/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJobTest.kt new file mode 100644 index 00000000..77d1f82c --- /dev/null +++ b/src/test/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJobTest.kt @@ -0,0 +1,58 @@ +package com.vauthenticator.server.job + +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import com.vauthenticator.server.keys.adapter.jdbc.JdbcKeyStorage +import com.vauthenticator.server.keys.domain.* +import com.vauthenticator.server.keys.domain.KeyPurpose.SIGNATURE +import com.vauthenticator.server.support.AccountTestFixture +import com.vauthenticator.server.support.ClientAppFixture +import com.vauthenticator.server.support.JdbcUtils.jdbcTemplate +import com.vauthenticator.server.support.JdbcUtils.resetDb +import com.vauthenticator.server.support.TicketFixture +import com.vauthenticator.server.ticket.adapter.jdbc.JdbcTicketRepository +import com.vauthenticator.server.ticket.domain.TicketId +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import java.time.Clock +import java.time.Duration +import java.util.* + + +class DatabaseTtlEntryCleanJobTest { + + @BeforeEach + fun setUp() { + resetDb() + } + + @Test + fun `when the old entries are deleted`() { + val ticketRepository = JdbcTicketRepository(jdbcTemplate, jacksonObjectMapper()) + val keyStorage = JdbcKeyStorage(jdbcTemplate, Clock.systemDefaultZone()) + + val uut = DatabaseTtlEntryCleanJob(jdbcTemplate, Clock.systemUTC()) + + val kid = Kid("") + val anAccount = AccountTestFixture.anAccount() + val aClientAppId = ClientAppFixture.aClientAppId() + + ticketRepository.store(TicketFixture.ticketFor("A_TICKET", anAccount.email, aClientAppId.content)) + keyStorage.store( + MasterKid(""), + kid, + DataKey(ByteArray(0), Optional.empty()), + KeyType.ASYMMETRIC, + SIGNATURE + ) + keyStorage.keyDeleteJodPlannedFor(kid, Duration.ofSeconds(-200), SIGNATURE) + + uut.execute() + + val actualTicket = ticketRepository.loadFor(TicketId("A_TICKET")) + Assertions.assertTrue(actualTicket.isEmpty) + Assertions.assertThrows(NoSuchElementException::class.java) { + keyStorage.findOne(kid, SIGNATURE) + } + } +} \ No newline at end of file From 74a9ba2ca98dbc48a7fe3192e8b1ba51304e948c Mon Sep 17 00:00:00 2001 From: mrflick72 Date: Fri, 22 Nov 2024 23:18:16 +0100 Subject: [PATCH 4/8] WIP lock implementation --- local-environment/application.yml | 5 ++ .../server/job/DatabaseTtlEntryCleanJob.kt | 51 +++++++++++++++++-- .../vauthenticator/server/job/LockService.kt | 42 +++++++++++++++ .../server/keys/adapter/java/KeyInitJob.kt | 5 +- .../job/DatabaseTtlEntryCleanJobTest.kt | 36 ++++++++++--- .../server/job/RedisLockServiceTest.kt | 31 +++++++++++ 6 files changed, 158 insertions(+), 12 deletions(-) create mode 100644 src/main/kotlin/com/vauthenticator/server/job/LockService.kt create mode 100644 src/test/kotlin/com/vauthenticator/server/job/RedisLockServiceTest.kt diff --git a/local-environment/application.yml b/local-environment/application.yml index fe1a62dc..cd69ae29 100644 --- a/local-environment/application.yml +++ b/local-environment/application.yml @@ -2,6 +2,11 @@ endSessionWithoutDiscovery: true oidcEndSessionUrl: ${vauthenticator.host}/oidc/logout auth.oidcIss: ${vauthenticator.host} +scheduled: + database-cleanup: + lock-ttl: 30000 + cron: "0 * * * * *" + event: consumer: enable: diff --git a/src/main/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJob.kt b/src/main/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJob.kt index 59e447d2..17c54836 100644 --- a/src/main/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJob.kt +++ b/src/main/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJob.kt @@ -1,29 +1,53 @@ package com.vauthenticator.server.job +import org.slf4j.LoggerFactory +import org.springframework.beans.factory.annotation.Value +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.context.annotation.Profile +import org.springframework.data.redis.core.RedisTemplate import org.springframework.jdbc.core.JdbcTemplate import org.springframework.jdbc.core.queryForList +import org.springframework.scheduling.annotation.EnableScheduling import org.springframework.scheduling.annotation.Scheduled +import org.springframework.stereotype.Repository import org.springframework.transaction.annotation.Transactional import java.time.Clock - @Transactional class DatabaseTtlEntryCleanJob( private val jdbcTemplate: JdbcTemplate, + private val lockTtl: Long, + private val lockService: LockService, private val clock: Clock ) { + private val logger = LoggerFactory.getLogger(DatabaseTtlEntryCleanJob::class.java) + @Scheduled(cron = "\${scheduled.database-cleanup.cron}") fun execute() { - val now = clock.instant().epochSecond + try { + logger.info("Try to Schedule") + + lockService.lock(lockTtl) + logger.info("Job Running") + val now = clock.instant().epochSecond + + deleteOldTicket(now) + deleteOldKeys(now) + logger.info("Job Completed") + } finally { + lockService.unlock() + } - deleteOldTicket(now) - deleteOldKeys(now) } private fun deleteOldKeys(now: Long) { val keysToBeDeleted = - jdbcTemplate.queryForList("SELECT key_id,key_purpose FROM KEYS WHERE key_expiration_date_timestamp < ?", now) + jdbcTemplate.queryForList( + "SELECT key_id,key_purpose FROM KEYS WHERE key_expiration_date_timestamp < ?", + now + ) keysToBeDeleted.forEach { jdbcTemplate.update( "DELETE FROM KEYS WHERE key_id = ? AND key_purpose = ?;", it["key_id"], it["key_purpose"] @@ -42,4 +66,21 @@ class DatabaseTtlEntryCleanJob( } } +} + + +@Profile("!kms") +@EnableScheduling +@Configuration(proxyBeanMethods = false) +class DatabaseTtlEntryCleanJobConfig { + + @Bean + fun databaseTtlEntryCleanJob( + @Value("\${scheduled.database-cleanup.lock-ttl}") lockTtl: Long, + lockService: LockService, + jdbcTemplate: JdbcTemplate + ) = DatabaseTtlEntryCleanJob(jdbcTemplate, lockTtl, lockService, Clock.systemUTC()) + + @Bean + fun lockService(redisTemplate: RedisTemplate) = RedisLockService(redisTemplate) } \ No newline at end of file diff --git a/src/main/kotlin/com/vauthenticator/server/job/LockService.kt b/src/main/kotlin/com/vauthenticator/server/job/LockService.kt new file mode 100644 index 00000000..9786dd05 --- /dev/null +++ b/src/main/kotlin/com/vauthenticator/server/job/LockService.kt @@ -0,0 +1,42 @@ +package com.vauthenticator.server.job + +import org.slf4j.LoggerFactory +import org.springframework.data.redis.core.RedisTemplate +import java.util.concurrent.TimeUnit + + +interface LockService { + + fun lock(timeout: Long) + + fun unlock() + +} + +class RedisLockService( + private val redisTemplate: RedisTemplate +) : LockService { + + private val logger = LoggerFactory.getLogger(RedisLockService::class.java) + + override fun lock(timeout: Long) { + if (!acquireLockWith(timeout)) { + logger.info("lock already acquired") + Thread.sleep(timeout) + } else { + logger.info("lock acquired") + } + } + + private fun acquireLockWith(timeout: Long): Boolean { + logger.info("try to acquire Lock") + return redisTemplate.opsForValue() + .setIfAbsent("lockKey", "locked", timeout, TimeUnit.MILLISECONDS)!! + } + + override fun unlock() { + logger.info("lock released") + redisTemplate.opsForValue().getAndDelete("lockKey") + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/vauthenticator/server/keys/adapter/java/KeyInitJob.kt b/src/main/kotlin/com/vauthenticator/server/keys/adapter/java/KeyInitJob.kt index 58077ddc..8ab29e21 100644 --- a/src/main/kotlin/com/vauthenticator/server/keys/adapter/java/KeyInitJob.kt +++ b/src/main/kotlin/com/vauthenticator/server/keys/adapter/java/KeyInitJob.kt @@ -1,6 +1,7 @@ package com.vauthenticator.server.keys.adapter.java import com.vauthenticator.server.keys.domain.* +import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Value import org.springframework.boot.ApplicationArguments import org.springframework.boot.ApplicationRunner @@ -15,6 +16,8 @@ class KeyInitJob( private val keyRepository: KeyRepository ) : ApplicationRunner { + val logger = LoggerFactory.getLogger(KeyInitJob::class.java) + override fun run(args: ApplicationArguments) { if (keyStorage.signatureKeys().keys.isEmpty()) { @@ -23,7 +26,7 @@ class KeyInitJob( keyPurpose = KeyPurpose.SIGNATURE, keyType = KeyType.ASYMMETRIC, ) - println(kid) + logger.info("Token Signature Key init job has been executed. Key ID generated: $kid") } } diff --git a/src/test/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJobTest.kt b/src/test/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJobTest.kt index 77d1f82c..591b8b88 100644 --- a/src/test/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJobTest.kt +++ b/src/test/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJobTest.kt @@ -2,8 +2,11 @@ package com.vauthenticator.server.job import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.vauthenticator.server.keys.adapter.jdbc.JdbcKeyStorage -import com.vauthenticator.server.keys.domain.* +import com.vauthenticator.server.keys.domain.DataKey import com.vauthenticator.server.keys.domain.KeyPurpose.SIGNATURE +import com.vauthenticator.server.keys.domain.KeyType +import com.vauthenticator.server.keys.domain.Kid +import com.vauthenticator.server.keys.domain.MasterKid import com.vauthenticator.server.support.AccountTestFixture import com.vauthenticator.server.support.ClientAppFixture import com.vauthenticator.server.support.JdbcUtils.jdbcTemplate @@ -11,16 +14,27 @@ import com.vauthenticator.server.support.JdbcUtils.resetDb import com.vauthenticator.server.support.TicketFixture import com.vauthenticator.server.ticket.adapter.jdbc.JdbcTicketRepository import com.vauthenticator.server.ticket.domain.TicketId -import org.junit.jupiter.api.Assertions +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.junit5.MockKExtension +import io.mockk.just +import io.mockk.runs +import io.mockk.verify +import org.junit.jupiter.api.Assertions.assertThrows +import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith import java.time.Clock import java.time.Duration import java.util.* - +@ExtendWith(MockKExtension::class) class DatabaseTtlEntryCleanJobTest { + @MockK + lateinit var lockService: LockService + @BeforeEach fun setUp() { resetDb() @@ -31,7 +45,7 @@ class DatabaseTtlEntryCleanJobTest { val ticketRepository = JdbcTicketRepository(jdbcTemplate, jacksonObjectMapper()) val keyStorage = JdbcKeyStorage(jdbcTemplate, Clock.systemDefaultZone()) - val uut = DatabaseTtlEntryCleanJob(jdbcTemplate, Clock.systemUTC()) + val uut = DatabaseTtlEntryCleanJob(jdbcTemplate, 100, lockService, Clock.systemUTC()) val kid = Kid("") val anAccount = AccountTestFixture.anAccount() @@ -47,11 +61,21 @@ class DatabaseTtlEntryCleanJobTest { ) keyStorage.keyDeleteJodPlannedFor(kid, Duration.ofSeconds(-200), SIGNATURE) + every { + lockService.lock(100) + lockService.unlock() + } just runs + uut.execute() + verify { + lockService.lock(100) + lockService.unlock() + } + val actualTicket = ticketRepository.loadFor(TicketId("A_TICKET")) - Assertions.assertTrue(actualTicket.isEmpty) - Assertions.assertThrows(NoSuchElementException::class.java) { + assertTrue(actualTicket.isEmpty) + assertThrows(NoSuchElementException::class.java) { keyStorage.findOne(kid, SIGNATURE) } } diff --git a/src/test/kotlin/com/vauthenticator/server/job/RedisLockServiceTest.kt b/src/test/kotlin/com/vauthenticator/server/job/RedisLockServiceTest.kt new file mode 100644 index 00000000..f4bad567 --- /dev/null +++ b/src/test/kotlin/com/vauthenticator/server/job/RedisLockServiceTest.kt @@ -0,0 +1,31 @@ +package com.vauthenticator.server.job + +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.junit5.MockKExtension +import io.mockk.mockk +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.springframework.data.redis.core.RedisTemplate +import org.springframework.data.redis.core.ValueOperations +import java.util.concurrent.TimeUnit + +@ExtendWith(MockKExtension::class) +class RedisLockServiceTest { + + @MockK + lateinit var redisTemplate: RedisTemplate + + @Test + fun `when lock and unlock`() { + val uut = RedisLockService(redisTemplate) + + every { redisTemplate.opsForValue() } returns mockk> { + every { setIfAbsent("lockKey", "locked", 100, TimeUnit.MILLISECONDS) } returns true + every { getAndDelete("lockKey") } returns "lockKey" + } + + uut.lock(100) + uut.unlock() + } +} \ No newline at end of file From 0b58f8dafb62c3e4c05e13a2c6eb97ecf2280f7a Mon Sep 17 00:00:00 2001 From: mrflick72 Date: Sat, 23 Nov 2024 23:01:41 +0100 Subject: [PATCH 5/8] remove the scheduling to k8s job that will use an actuator endpoint --- local-environment/http-client.env.json | 3 +- local-environment/request.http | 8 ++++ .../server/job/DatabaseTtlEntryCleanJob.kt | 35 ++++------------ .../job/DatabaseTtlEntryCleanJobEndPoint.kt | 17 ++++++++ .../vauthenticator/server/job/LockService.kt | 42 ------------------- .../job/DatabaseTtlEntryCleanJobTest.kt | 21 +--------- .../server/job/RedisLockServiceTest.kt | 31 -------------- 7 files changed, 37 insertions(+), 120 deletions(-) create mode 100644 src/main/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJobEndPoint.kt delete mode 100644 src/main/kotlin/com/vauthenticator/server/job/LockService.kt delete mode 100644 src/test/kotlin/com/vauthenticator/server/job/RedisLockServiceTest.kt diff --git a/local-environment/http-client.env.json b/local-environment/http-client.env.json index f0f9caa8..e5583fe3 100644 --- a/local-environment/http-client.env.json +++ b/local-environment/http-client.env.json @@ -1,5 +1,6 @@ { "LOCAL": { - "host": "http://local.management.vauthenticator.com:9090" + "host": "http://local.management.vauthenticator.com:9090", + "actuatorHost": "http://local.management.vauthenticator.com:9091" } } \ No newline at end of file diff --git a/local-environment/request.http b/local-environment/request.http index 4b675909..06ae8efe 100644 --- a/local-environment/request.http +++ b/local-environment/request.http @@ -1,3 +1,11 @@ +### actuator root +GET {{actuatorHost}}/actuator + + +### acgtuator database-clean-up job +POST {{actuatorHost}}/actuator/database-clean-up + + ### login page GET {{host}}/login diff --git a/src/main/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJob.kt b/src/main/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJob.kt index 17c54836..9d535c5c 100644 --- a/src/main/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJob.kt +++ b/src/main/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJob.kt @@ -1,44 +1,29 @@ package com.vauthenticator.server.job import org.slf4j.LoggerFactory -import org.springframework.beans.factory.annotation.Value import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.context.annotation.Profile -import org.springframework.data.redis.core.RedisTemplate import org.springframework.jdbc.core.JdbcTemplate import org.springframework.jdbc.core.queryForList -import org.springframework.scheduling.annotation.EnableScheduling -import org.springframework.scheduling.annotation.Scheduled -import org.springframework.stereotype.Repository import org.springframework.transaction.annotation.Transactional import java.time.Clock @Transactional class DatabaseTtlEntryCleanJob( private val jdbcTemplate: JdbcTemplate, - private val lockTtl: Long, - private val lockService: LockService, private val clock: Clock ) { private val logger = LoggerFactory.getLogger(DatabaseTtlEntryCleanJob::class.java) - @Scheduled(cron = "\${scheduled.database-cleanup.cron}") fun execute() { - try { - logger.info("Try to Schedule") + logger.info("Job Running") + val now = clock.instant().epochSecond - lockService.lock(lockTtl) - logger.info("Job Running") - val now = clock.instant().epochSecond - - deleteOldTicket(now) - deleteOldKeys(now) - logger.info("Job Completed") - } finally { - lockService.unlock() - } + deleteOldTicket(now) + deleteOldKeys(now) + logger.info("Job Completed") } @@ -70,17 +55,15 @@ class DatabaseTtlEntryCleanJob( @Profile("!kms") -@EnableScheduling @Configuration(proxyBeanMethods = false) -class DatabaseTtlEntryCleanJobConfig { +class DatabaseTtlEntryCleanJobConfig() { @Bean fun databaseTtlEntryCleanJob( - @Value("\${scheduled.database-cleanup.lock-ttl}") lockTtl: Long, - lockService: LockService, jdbcTemplate: JdbcTemplate - ) = DatabaseTtlEntryCleanJob(jdbcTemplate, lockTtl, lockService, Clock.systemUTC()) + ) = DatabaseTtlEntryCleanJob(jdbcTemplate, Clock.systemUTC()) @Bean - fun lockService(redisTemplate: RedisTemplate) = RedisLockService(redisTemplate) + fun databaseTtlEntryCleanJobEndPoint(databaseTtlEntryCleanJob: DatabaseTtlEntryCleanJob) = + DatabaseTtlEntryCleanJobEndPoint(databaseTtlEntryCleanJob) } \ No newline at end of file diff --git a/src/main/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJobEndPoint.kt b/src/main/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJobEndPoint.kt new file mode 100644 index 00000000..20104a4e --- /dev/null +++ b/src/main/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJobEndPoint.kt @@ -0,0 +1,17 @@ +package com.vauthenticator.server.job + +import org.springframework.boot.actuate.endpoint.annotation.Endpoint +import org.springframework.boot.actuate.endpoint.annotation.WriteOperation +import org.springframework.http.ResponseEntity + +@Endpoint(id = "database-clean-up") +class DatabaseTtlEntryCleanJobEndPoint( + private val databaseTtlEntryCleanJob: DatabaseTtlEntryCleanJob +) { + + @WriteOperation + fun cleanUp(): ResponseEntity { + databaseTtlEntryCleanJob.execute() + return ResponseEntity.noContent().build() + } +} diff --git a/src/main/kotlin/com/vauthenticator/server/job/LockService.kt b/src/main/kotlin/com/vauthenticator/server/job/LockService.kt deleted file mode 100644 index 9786dd05..00000000 --- a/src/main/kotlin/com/vauthenticator/server/job/LockService.kt +++ /dev/null @@ -1,42 +0,0 @@ -package com.vauthenticator.server.job - -import org.slf4j.LoggerFactory -import org.springframework.data.redis.core.RedisTemplate -import java.util.concurrent.TimeUnit - - -interface LockService { - - fun lock(timeout: Long) - - fun unlock() - -} - -class RedisLockService( - private val redisTemplate: RedisTemplate -) : LockService { - - private val logger = LoggerFactory.getLogger(RedisLockService::class.java) - - override fun lock(timeout: Long) { - if (!acquireLockWith(timeout)) { - logger.info("lock already acquired") - Thread.sleep(timeout) - } else { - logger.info("lock acquired") - } - } - - private fun acquireLockWith(timeout: Long): Boolean { - logger.info("try to acquire Lock") - return redisTemplate.opsForValue() - .setIfAbsent("lockKey", "locked", timeout, TimeUnit.MILLISECONDS)!! - } - - override fun unlock() { - logger.info("lock released") - redisTemplate.opsForValue().getAndDelete("lockKey") - } - -} \ No newline at end of file diff --git a/src/test/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJobTest.kt b/src/test/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJobTest.kt index 591b8b88..fa8e7a4a 100644 --- a/src/test/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJobTest.kt +++ b/src/test/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJobTest.kt @@ -14,27 +14,16 @@ import com.vauthenticator.server.support.JdbcUtils.resetDb import com.vauthenticator.server.support.TicketFixture import com.vauthenticator.server.ticket.adapter.jdbc.JdbcTicketRepository import com.vauthenticator.server.ticket.domain.TicketId -import io.mockk.every -import io.mockk.impl.annotations.MockK -import io.mockk.junit5.MockKExtension -import io.mockk.just -import io.mockk.runs -import io.mockk.verify import org.junit.jupiter.api.Assertions.assertThrows import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith import java.time.Clock import java.time.Duration import java.util.* -@ExtendWith(MockKExtension::class) class DatabaseTtlEntryCleanJobTest { - @MockK - lateinit var lockService: LockService - @BeforeEach fun setUp() { resetDb() @@ -45,7 +34,7 @@ class DatabaseTtlEntryCleanJobTest { val ticketRepository = JdbcTicketRepository(jdbcTemplate, jacksonObjectMapper()) val keyStorage = JdbcKeyStorage(jdbcTemplate, Clock.systemDefaultZone()) - val uut = DatabaseTtlEntryCleanJob(jdbcTemplate, 100, lockService, Clock.systemUTC()) + val uut = DatabaseTtlEntryCleanJob(jdbcTemplate, Clock.systemUTC()) val kid = Kid("") val anAccount = AccountTestFixture.anAccount() @@ -61,17 +50,9 @@ class DatabaseTtlEntryCleanJobTest { ) keyStorage.keyDeleteJodPlannedFor(kid, Duration.ofSeconds(-200), SIGNATURE) - every { - lockService.lock(100) - lockService.unlock() - } just runs uut.execute() - verify { - lockService.lock(100) - lockService.unlock() - } val actualTicket = ticketRepository.loadFor(TicketId("A_TICKET")) assertTrue(actualTicket.isEmpty) diff --git a/src/test/kotlin/com/vauthenticator/server/job/RedisLockServiceTest.kt b/src/test/kotlin/com/vauthenticator/server/job/RedisLockServiceTest.kt deleted file mode 100644 index f4bad567..00000000 --- a/src/test/kotlin/com/vauthenticator/server/job/RedisLockServiceTest.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.vauthenticator.server.job - -import io.mockk.every -import io.mockk.impl.annotations.MockK -import io.mockk.junit5.MockKExtension -import io.mockk.mockk -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.springframework.data.redis.core.RedisTemplate -import org.springframework.data.redis.core.ValueOperations -import java.util.concurrent.TimeUnit - -@ExtendWith(MockKExtension::class) -class RedisLockServiceTest { - - @MockK - lateinit var redisTemplate: RedisTemplate - - @Test - fun `when lock and unlock`() { - val uut = RedisLockService(redisTemplate) - - every { redisTemplate.opsForValue() } returns mockk> { - every { setIfAbsent("lockKey", "locked", 100, TimeUnit.MILLISECONDS) } returns true - every { getAndDelete("lockKey") } returns "lockKey" - } - - uut.lock(100) - uut.unlock() - } -} \ No newline at end of file From bcb22c0fc60f7ec6da810bb6336401d731089669 Mon Sep 17 00:00:00 2001 From: mrflick72 Date: Sat, 23 Nov 2024 23:22:00 +0100 Subject: [PATCH 6/8] docs --- README.md | 1 + docs/management.md | 15 +++++++++++++++ local-environment/application.yml | 5 ----- .../DatabaseTtlEntryCleanJob.kt | 4 ++-- .../DatabaseTtlEntryCleanJobEndPoint.kt | 2 +- .../DatabaseTtlEntryCleanJobTest.kt | 2 +- 6 files changed, 20 insertions(+), 9 deletions(-) create mode 100644 docs/management.md rename src/main/kotlin/com/vauthenticator/server/{job => management}/DatabaseTtlEntryCleanJob.kt (96%) rename src/main/kotlin/com/vauthenticator/server/{job => management}/DatabaseTtlEntryCleanJobEndPoint.kt (91%) rename src/test/kotlin/com/vauthenticator/server/{job => management}/DatabaseTtlEntryCleanJobTest.kt (98%) diff --git a/README.md b/README.md index 606ce865..23114124 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ Right now it is based, as said before to the latest version on spring oauth2/ope - Post login flow - force to reset password - back/front channel logout +- management api: custom actuator endpoint for more details [look here](local-environment%2Fmanagement.md) **Storage:** diff --git a/docs/management.md b/docs/management.md new file mode 100644 index 00000000..78045d53 --- /dev/null +++ b/docs/management.md @@ -0,0 +1,15 @@ +# Management + +Custom Actuator Endpoint + +## Clean Database Entry with TTL + +Actuator Clean Database enabled for database profile activated + +*URI:* ```Post /actuator/database-clean-up``` + +*Request:* Empty request body + +*Response Status:* ```204 NoContent``` + +``` \ No newline at end of file diff --git a/local-environment/application.yml b/local-environment/application.yml index cd69ae29..fe1a62dc 100644 --- a/local-environment/application.yml +++ b/local-environment/application.yml @@ -2,11 +2,6 @@ endSessionWithoutDiscovery: true oidcEndSessionUrl: ${vauthenticator.host}/oidc/logout auth.oidcIss: ${vauthenticator.host} -scheduled: - database-cleanup: - lock-ttl: 30000 - cron: "0 * * * * *" - event: consumer: enable: diff --git a/src/main/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJob.kt b/src/main/kotlin/com/vauthenticator/server/management/DatabaseTtlEntryCleanJob.kt similarity index 96% rename from src/main/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJob.kt rename to src/main/kotlin/com/vauthenticator/server/management/DatabaseTtlEntryCleanJob.kt index 9d535c5c..418157a2 100644 --- a/src/main/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJob.kt +++ b/src/main/kotlin/com/vauthenticator/server/management/DatabaseTtlEntryCleanJob.kt @@ -1,4 +1,4 @@ -package com.vauthenticator.server.job +package com.vauthenticator.server.management import org.slf4j.LoggerFactory import org.springframework.context.annotation.Bean @@ -54,7 +54,7 @@ class DatabaseTtlEntryCleanJob( } -@Profile("!kms") +@Profile("database") @Configuration(proxyBeanMethods = false) class DatabaseTtlEntryCleanJobConfig() { diff --git a/src/main/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJobEndPoint.kt b/src/main/kotlin/com/vauthenticator/server/management/DatabaseTtlEntryCleanJobEndPoint.kt similarity index 91% rename from src/main/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJobEndPoint.kt rename to src/main/kotlin/com/vauthenticator/server/management/DatabaseTtlEntryCleanJobEndPoint.kt index 20104a4e..c0b846aa 100644 --- a/src/main/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJobEndPoint.kt +++ b/src/main/kotlin/com/vauthenticator/server/management/DatabaseTtlEntryCleanJobEndPoint.kt @@ -1,4 +1,4 @@ -package com.vauthenticator.server.job +package com.vauthenticator.server.management import org.springframework.boot.actuate.endpoint.annotation.Endpoint import org.springframework.boot.actuate.endpoint.annotation.WriteOperation diff --git a/src/test/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJobTest.kt b/src/test/kotlin/com/vauthenticator/server/management/DatabaseTtlEntryCleanJobTest.kt similarity index 98% rename from src/test/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJobTest.kt rename to src/test/kotlin/com/vauthenticator/server/management/DatabaseTtlEntryCleanJobTest.kt index fa8e7a4a..7e7c49ed 100644 --- a/src/test/kotlin/com/vauthenticator/server/job/DatabaseTtlEntryCleanJobTest.kt +++ b/src/test/kotlin/com/vauthenticator/server/management/DatabaseTtlEntryCleanJobTest.kt @@ -1,4 +1,4 @@ -package com.vauthenticator.server.job +package com.vauthenticator.server.management import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.vauthenticator.server.keys.adapter.jdbc.JdbcKeyStorage From 123e9f378b0db81ff047c17c9b3d0c6045b6502b Mon Sep 17 00:00:00 2001 From: mrflick72 Date: Sat, 23 Nov 2024 23:22:53 +0100 Subject: [PATCH 7/8] docs --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 23114124..f4b8f210 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Right now it is based, as said before to the latest version on spring oauth2/ope - Post login flow - force to reset password - back/front channel logout -- management api: custom actuator endpoint for more details [look here](local-environment%2Fmanagement.md) +- management api: custom actuator endpoint for more details [look here](docs/management.md) **Storage:** From 15b9db74e6de7ffc13ef76aaf142fb1eecf90123 Mon Sep 17 00:00:00 2001 From: mrflick72 Date: Sat, 23 Nov 2024 23:23:54 +0100 Subject: [PATCH 8/8] docs --- docs/management.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/management.md b/docs/management.md index 78045d53..c37086b8 100644 --- a/docs/management.md +++ b/docs/management.md @@ -11,5 +11,3 @@ Actuator Clean Database enabled for database profile activated *Request:* Empty request body *Response Status:* ```204 NoContent``` - -``` \ No newline at end of file