Skip to content

Commit

Permalink
Merge pull request #260 from VAuthenticator/clean-up-entry-with-ttl-job
Browse files Browse the repository at this point in the history
Clean up entry with ttl job
  • Loading branch information
mrFlick72 authored Nov 23, 2024
2 parents 7755854 + 15b9db7 commit 2fcafdf
Show file tree
Hide file tree
Showing 9 changed files with 179 additions and 4 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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](docs/management.md)

**Storage:**

Expand Down
13 changes: 13 additions & 0 deletions docs/management.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# 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```
3 changes: 2 additions & 1 deletion local-environment/http-client.env.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
8 changes: 8 additions & 0 deletions local-environment/request.http
Original file line number Diff line number Diff line change
@@ -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

Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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()) {
Expand All @@ -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")
}

}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.vauthenticator.server.management

import org.slf4j.LoggerFactory
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Profile
import org.springframework.jdbc.core.JdbcTemplate
import org.springframework.jdbc.core.queryForList
import org.springframework.transaction.annotation.Transactional
import java.time.Clock

@Transactional
class DatabaseTtlEntryCleanJob(
private val jdbcTemplate: JdbcTemplate,
private val clock: Clock
) {

private val logger = LoggerFactory.getLogger(DatabaseTtlEntryCleanJob::class.java)

fun execute() {
logger.info("Job Running")
val now = clock.instant().epochSecond

deleteOldTicket(now)
deleteOldKeys(now)
logger.info("Job Completed")

}

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<String>(
"SELECT ticket FROM TICKET WHERE ttl < ?",
arrayOf(now)
)
ticketToBeDeleted.forEach {
jdbcTemplate.update("DELETE FROM TICKET WHERE ticket = ?", it)
}
}

}


@Profile("database")
@Configuration(proxyBeanMethods = false)
class DatabaseTtlEntryCleanJobConfig() {

@Bean
fun databaseTtlEntryCleanJob(
jdbcTemplate: JdbcTemplate
) = DatabaseTtlEntryCleanJob(jdbcTemplate, Clock.systemUTC())

@Bean
fun databaseTtlEntryCleanJobEndPoint(databaseTtlEntryCleanJob: DatabaseTtlEntryCleanJob) =
DatabaseTtlEntryCleanJobEndPoint(databaseTtlEntryCleanJob)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.vauthenticator.server.management

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<Unit> {
databaseTtlEntryCleanJob.execute()
return ResponseEntity.noContent().build()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, String>,
) as Map<String, String>).orElse(emptyMap()),
autoApprove = AutoApprove(rs.getBoolean("auto_approve")),
postLogoutRedirectUri = PostLogoutRedirectUri(rs.getString("post_logout_redirect_uri")),
logoutUri = LogoutUri(rs.getString("logout_uri"))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.vauthenticator.server.management

import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.vauthenticator.server.keys.adapter.jdbc.JdbcKeyStorage
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
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.assertThrows
import org.junit.jupiter.api.Assertions.assertTrue
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"))
assertTrue(actualTicket.isEmpty)
assertThrows(NoSuchElementException::class.java) {
keyStorage.findOne(kid, SIGNATURE)
}
}
}

0 comments on commit 2fcafdf

Please sign in to comment.