Skip to content

Commit

Permalink
Update Java, Quarkus & Spotless
Browse files Browse the repository at this point in the history
  • Loading branch information
Brutus5000 committed Jul 1, 2024
1 parent 8af51f8 commit c708ebf
Show file tree
Hide file tree
Showing 19 changed files with 205 additions and 155 deletions.
15 changes: 6 additions & 9 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget

plugins {
val kotlinVersion = "2.0.0"

Expand Down Expand Up @@ -47,8 +49,6 @@ group = "com.faforever.icebreaker"
version = "1.0.0-SNAPSHOT"

java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}

tasks.withType<Test> {
Expand All @@ -61,12 +61,9 @@ allOpen {
}

tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
kotlinOptions {
jvmTarget = JavaVersion.VERSION_17.toString()
javaParameters = true

// For creation of default methods in interfaces
freeCompilerArgs = listOf("-Xjvm-default=all")
compilerOptions {
jvmTarget.set(JvmTarget.JVM_21)
javaParameters.set(true)
}
}

Expand Down Expand Up @@ -96,7 +93,7 @@ noArg {
}

spotless {
val ktlintVersion = "0.49.1"
val ktlintVersion = "1.3.0"
kotlin {
ktlint(ktlintVersion)
}
Expand Down
10 changes: 4 additions & 6 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
#Gradle properties
#Sun Nov 19 14:11:53 CET 2023
quarkusPlatformArtifactId=quarkus-bom
quarkusPlatformGroupId=io.quarkus.platform
quarkusPlatformVersion=3.5.0
quarkusPluginId=io.quarkus
quarkusPluginVersion=3.5.0
quarkusPluginVersion=3.12.0
quarkusPlatformGroupId=io.quarkus.platform
quarkusPlatformArtifactId=quarkus-bom
quarkusPlatformVersion=3.12.0
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import jakarta.inject.Singleton

@Singleton
class AddKotlinModuleCustomizer : ObjectMapperCustomizer {

override fun customize(objectMapper: ObjectMapper) {
objectMapper.registerModule(KotlinModule.Builder().build())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,12 @@ data class CoturnServerEntity(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long,

val region: String,

val host: String,

val port: Int,

val presharedKey: String,

val contactEmail: String,

val active: Boolean,

) : PanacheEntityBase

@Singleton
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,42 +16,44 @@ import java.util.concurrent.TimeoutException
data class IceSessionEntity(
@Id
val id: String = UUID.randomUUID().toString(),

val gameId: Long,

val createdAt: Instant,

) : PanacheEntityBase

private val LOG = LoggerFactory.getLogger(IceSessionRepository::class.java)

@Singleton
class IceSessionRepository : PanacheRepository<IceSessionEntity> {
fun existsByGameId(gameId: Long) = count("gameId = ?1", gameId) != 0L

fun existsByGameId(gameId: Long) =
count("gameId = ?1", gameId) != 0L

fun findByGameId(gameId: Long) =
find("gameId = ?1", gameId).firstResult()
fun findByGameId(gameId: Long) = find("gameId = ?1", gameId).firstResult()

fun findByCreatedAtLesserThan(instant: Instant) =
find("createdAt <= ?1", instant).list()
fun findByCreatedAtLesserThan(instant: Instant) = find("createdAt <= ?1", instant).list()

fun acquireGameLock(gameId: Long, timeout: Int = 10) {
val lockAcquired = getEntityManager().createNativeQuery("SELECT GET_LOCK(:lockName,:timeout)", Boolean::class.java).apply {
setParameter("lockName", "game_id_$gameId")
setParameter("timeout", timeout)
}.singleResult as Boolean?
fun acquireGameLock(
gameId: Long,
timeout: Int = 10,
) {
val lockAcquired =
getEntityManager()
.createNativeQuery("SELECT GET_LOCK(:lockName,:timeout)", Boolean::class.java)
.apply {
setParameter("lockName", "game_id_$gameId")
setParameter("timeout", timeout)
}.singleResult as Boolean?

if (lockAcquired != true) {
throw TimeoutException("Unable to acquire game lock for $gameId")
}
}

fun releaseGameLock(gameId: Long) {
val lockReleased = getEntityManager().createNativeQuery("SELECT RELEASE_LOCK(:lockName)", Boolean::class.java).apply {
setParameter("lockName", "game_id_$gameId")
}.singleResult as Boolean?
val lockReleased =
getEntityManager()
.createNativeQuery("SELECT RELEASE_LOCK(:lockName)", Boolean::class.java)
.apply {
setParameter("lockName", "game_id_$gameId")
}.singleResult as Boolean?

when (lockReleased) {
null -> LOG.warn("No lock exists for $gameId")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,20 @@ class CustomTenantResolver(
private val fafProperties: FafProperties,
private val objectMapper: ObjectMapper,
) : TenantResolver {

override fun resolve(context: RoutingContext): String? =
context.request().getHeader(AUTHORIZATION)
context
.request()
.getHeader(AUTHORIZATION)
?.takeIf { it.startsWith("Bearer ") }
?.let {
val rawToken = it.substring(7)
val body = java.util.Base64.getDecoder().decode(rawToken.split(".")[1])
val body =
java.util.Base64
.getDecoder()
.decode(rawToken.split(".")[1])
val json = objectMapper.readTree(body)

json["iss"]?.textValue()
}
?.takeIf { it == fafProperties.selfUrl() }
}?.takeIf { it == fafProperties.selfUrl() }
?.let { "self-tenant" }
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,35 @@ import org.eclipse.microprofile.jwt.JsonWebToken

@ApplicationScoped
class FafPermissionsAugmentor : SecurityIdentityAugmentor {
override fun augment(identity: SecurityIdentity, context: AuthenticationRequestContext): Uni<SecurityIdentity> {
return Uni.createFrom().item(build(identity))
}
override fun augment(
identity: SecurityIdentity,
context: AuthenticationRequestContext,
): Uni<SecurityIdentity> = Uni.createFrom().item(build(identity))

@Suppress("UNCHECKED_CAST")
private fun build(identity: SecurityIdentity): SecurityIdentity {
return if (identity.isAnonymous) {
private fun build(identity: SecurityIdentity): SecurityIdentity =
if (identity.isAnonymous) {
identity
} else {
val builder = QuarkusSecurityIdentity.builder(identity)
when (val principal = identity.principal) {
is JsonWebToken -> {
val roles = principal.claim<Map<String, Any>>("ext")
.map<List<JsonString>> { it["roles"] as? List<JsonString> }
.map { it.map { jsonString -> jsonString.string } }
.map { it.toSet() }
.orElse(setOf())
val roles =
principal
.claim<Map<String, Any>>("ext")
.map<List<JsonString>> { it["roles"] as? List<JsonString> }
.map { it.map { jsonString -> jsonString.string } }
.map { it.toSet() }
.orElse(setOf())

val scopes = principal.claim<List<JsonString>>("scp")
.map { it.map { jsonString -> jsonString.string }.toSet() }
.orElse(setOf())
val scopes =
principal
.claim<List<JsonString>>("scp")
.map { it.map { jsonString -> jsonString.string }.toSet() }
.orElse(setOf())

principal.claim<Map<String, Any>>("ext")
principal
.claim<Map<String, Any>>("ext")
.map<JsonNumber> { it["gameId"] as? JsonNumber }
.ifPresent {
builder.addAttribute("gameId", it.longValue())
Expand All @@ -49,5 +55,4 @@ class FafPermissionsAugmentor : SecurityIdentityAugmentor {
}
builder.build()
}
}
}
46 changes: 26 additions & 20 deletions src/main/kotlin/com/faforever/icebreaker/service/SessionService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,22 @@ class SessionService(
private val activeSessionHandlers = sessionHandlers.filter { it.active }

fun buildToken(gameId: Long): String {
val userId = when (val principal = securityIdentity?.principal) {
null -> throw UnauthorizedException("No principal available")
is JsonWebToken -> principal.subject.toInt()
else -> throw IllegalStateException("Unexpected principal type: ${principal.javaClass} ($principal)")
}
val userId =
when (val principal = securityIdentity?.principal) {
null -> throw UnauthorizedException("No principal available")
is JsonWebToken -> principal.subject.toInt()
else -> throw IllegalStateException("Unexpected principal type: ${principal.javaClass} ($principal)")
}

return Jwt.subject(userId.toString())
return Jwt
.subject(userId.toString())
.claim(
"ext",
mapOf(
"roles" to listOf("USER"),
"gameId" to gameId,
),
)
.claim("scp", listOf("lobby"))
).claim("scp", listOf("lobby"))
.issuer(fafProperties.selfUrl())
.audience(fafProperties.selfUrl())
.expiresAt(Instant.now().plus(fafProperties.maxSessionLifeTimeHours(), ChronoUnit.HOURS))
Expand All @@ -66,10 +67,11 @@ class SessionService(

val sessionId = "game/$gameId"

val servers = activeSessionHandlers.flatMap {
it.createSession(sessionId)
it.getIceServersSession(sessionId)
}
val servers =
activeSessionHandlers.flatMap {
it.createSession(sessionId)
it.getIceServersSession(sessionId)
}

AsyncRunner.runLater {
persistSessionDetailsIfNecessary(gameId, sessionId)
Expand All @@ -82,7 +84,10 @@ class SessionService(
}

@Transactional
fun persistSessionDetailsIfNecessary(gameId: Long, sessionId: String) {
fun persistSessionDetailsIfNecessary(
gameId: Long,
sessionId: String,
) {
if (!iceSessionRepository.existsByGameId(gameId)) {
try {
LOG.debug("Creating session for gameId $gameId")
Expand All @@ -103,12 +108,13 @@ class SessionService(
@Scheduled(every = "10m")
fun cleanUpSessions() {
LOG.info("Cleaning up outdated sessions")
iceSessionRepository.findByCreatedAtLesserThan(
instant = Instant.now().plus(fafProperties.maxSessionLifeTimeHours(), ChronoUnit.HOURS),
).forEach { iceSession ->
LOG.debug("Cleaning up session id ${iceSession.id}")
activeSessionHandlers.forEach { it.deleteSession(iceSession.id) }
iceSessionRepository.delete(iceSession)
}
iceSessionRepository
.findByCreatedAtLesserThan(
instant = Instant.now().plus(fafProperties.maxSessionLifeTimeHours(), ChronoUnit.HOURS),
).forEach { iceSession ->
LOG.debug("Cleaning up session id ${iceSession.id}")
activeSessionHandlers.forEach { it.deleteSession(iceSession.id) }
iceSessionRepository.delete(iceSession)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ class CoturnSessionHandler(
// Coturn has no session handling, we use global access
}

override fun getIceServers() =
coturnServerRepository.findActive().map { Server(id = it.host, region = it.region) }
override fun getIceServers() = coturnServerRepository.findActive().map { Server(id = it.host, region = it.region) }

override fun getIceServersSession(sessionId: String): List<Session.Server> =
coturnServerRepository.findActive()
coturnServerRepository
.findActive()
.map {
val (tokenName, tokenSecret) = buildHmac(sessionId, it.presharedKey)
Session.Server(
Expand All @@ -41,22 +41,29 @@ class CoturnSessionHandler(
)
}

private fun buildHmac(sessionName: String, presharedKey: String): Pair<String, String> {
private fun buildHmac(
sessionName: String,
presharedKey: String,
): Pair<String, String> {
val timestamp = System.currentTimeMillis() / 1000 + fafProperties.tokenLifetimeSeconds()
val tokenName = "$timestamp:$sessionName"

val secretKeySpec = SecretKeySpec(presharedKey.encodeToByteArray(), "HmacSHA1")
val mac = Mac.getInstance("HmacSHA1")
mac.init(secretKeySpec)

val tokenSecret = mac.doFinal(tokenName.encodeToByteArray()).let {
Base64.getEncoder().encodeToString(it)
}
val tokenSecret =
mac.doFinal(tokenName.encodeToByteArray()).let {
Base64.getEncoder().encodeToString(it)
}

return tokenName to tokenSecret
}

private fun buildUrls(hostName: String, port: Int) = listOf(
private fun buildUrls(
hostName: String,
port: Int,
) = listOf(
"stun://$hostName:$port",
"turn://$hostName:$port?transport=udp",
"turn://$hostName:$port?transport=tcp",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,16 @@ class XirsysApiAdapter(
private val xirsysProperties: XirsysProperties,
private val objectMapper: ObjectMapper,
) {
private val xirsysApiClient: XirsysApiClient = RestClientBuilder.newBuilder()
.baseUri(URI.create(xirsysProperties.baseUrl()))
.register(
BasicAuthenticationRequestFilter(
username = xirsysProperties.ident(),
password = xirsysProperties.secret(),
),
)
.build(XirsysApiClient::class.java)
private val xirsysApiClient: XirsysApiClient =
RestClientBuilder
.newBuilder()
.baseUri(URI.create(xirsysProperties.baseUrl()))
.register(
BasicAuthenticationRequestFilter(
username = xirsysProperties.ident(),
password = xirsysProperties.secret(),
),
).build(XirsysApiClient::class.java)

@Retry
fun listChannel(): List<String> =
Expand Down Expand Up @@ -75,7 +76,10 @@ class XirsysApiAdapter(
}

@Retry
fun requestIceServers(channelName: String, turnRequest: TurnRequest = TurnRequest()): TurnResponse =
fun requestIceServers(
channelName: String,
turnRequest: TurnRequest = TurnRequest(),
): TurnResponse =
parseAndUnwrap {
xirsysApiClient.requestIceServers(
namespace = xirsysProperties.channelNamespace(),
Expand Down
Loading

0 comments on commit c708ebf

Please sign in to comment.