fix(official-bots): configure JWT verification #74
@@ -12,6 +12,12 @@ quarkus:
|
|||||||
enabled: true
|
enabled: true
|
||||||
log:
|
log:
|
||||||
level: INFO
|
level: INFO
|
||||||
|
mp:
|
||||||
|
jwt:
|
||||||
|
verify:
|
||||||
|
publickey:
|
||||||
|
location: ${JWT_PUBLIC_KEY_PATH:keys/public.pem}
|
||||||
|
issuer: nowchess
|
||||||
|
|
||||||
nowchess:
|
nowchess:
|
||||||
redis:
|
redis:
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
-----BEGIN PUBLIC KEY-----
|
||||||
|
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxDsnsCAl0vQx7Vu9CLDZ
|
||||||
|
g0SG05NgUzu9T+3DTEaHGq60T2uriO8BenwyvsF3BnDqTbKf4voohZ1DNfzdbT1J
|
||||||
|
Fj8B62FrDmxcO+sp1/b5HUCJP6y2uSRCmzOHe5k7Pk1IEi72FgBpKXSRkFibRlVf
|
||||||
|
634g7mgsPZAQ9PJEsv4Qvm05T9L6+Gmq6N3bMVLKRXs4RhDhaFbYH9GtUg1eI0yH
|
||||||
|
YjGyRfqzW/nqVMstOLHt8CuPouq4p7eMzeDH3YHkxPm4GG5foCXMOd2DZrW0SCcr
|
||||||
|
7dhFeNVWzQ2m53eOhBzNQX+v3pgjVStsePhBRt2LyGfwkNzmqDgqWsMzSHRMY+cn
|
||||||
|
WQIDAQAB
|
||||||
|
-----END PUBLIC KEY-----
|
||||||
+1
-1
@@ -20,7 +20,7 @@ object TournamentBotConfig:
|
|||||||
tournamentId <- env.get("TOURNAMENT_ID").filter(_.nonEmpty)
|
tournamentId <- env.get("TOURNAMENT_ID").filter(_.nonEmpty)
|
||||||
token <- env.get("TOURNAMENT_BOT_TOKEN").filter(_.nonEmpty)
|
token <- env.get("TOURNAMENT_BOT_TOKEN").filter(_.nonEmpty)
|
||||||
botId <- jwtSubject(token)
|
botId <- jwtSubject(token)
|
||||||
serverUrl = env.getOrElse("TOURNAMENT_SERVER_URL", "http://localhost:8089")
|
serverUrl = env.getOrElse("TOURNAMENT_SERVER_URL", "http://141.37.123.132:8086")
|
||||||
difficulty = env.getOrElse("TOURNAMENT_BOT_DIFFICULTY", "medium")
|
difficulty = env.getOrElse("TOURNAMENT_BOT_DIFFICULTY", "medium")
|
||||||
yield TournamentBotConfig(serverUrl, tournamentId, token, botId, difficulty)
|
yield TournamentBotConfig(serverUrl, tournamentId, token, botId, difficulty)
|
||||||
|
|
||||||
|
|||||||
+34
-3
@@ -39,10 +39,11 @@ class TournamentBotGamePlayer:
|
|||||||
// scalafix:on DisableSyntax.var
|
// scalafix:on DisableSyntax.var
|
||||||
|
|
||||||
val defaultServerUrl: String =
|
val defaultServerUrl: String =
|
||||||
System.getenv().asScala.getOrElse("TOURNAMENT_SERVER_URL", "http://localhost:8089")
|
System.getenv().asScala.getOrElse("TOURNAMENT_SERVER_URL", "http://141.37.123.132:8086")
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
def initialize(): Unit =
|
def initialize(): Unit =
|
||||||
|
parkOnStartup()
|
||||||
config match
|
config match
|
||||||
case None =>
|
case None =>
|
||||||
log.info("Tournament bot disabled — set TOURNAMENT_ID and TOURNAMENT_BOT_TOKEN to enable")
|
log.info("Tournament bot disabled — set TOURNAMENT_ID and TOURNAMENT_BOT_TOKEN to enable")
|
||||||
@@ -50,6 +51,35 @@ class TournamentBotGamePlayer:
|
|||||||
log.infof("Tournament bot enabled — server=%s tournament=%s bot=%s", cfg.serverUrl, cfg.tournamentId, cfg.botId)
|
log.infof("Tournament bot enabled — server=%s tournament=%s bot=%s", cfg.serverUrl, cfg.tournamentId, cfg.botId)
|
||||||
startAsync(cfg)
|
startAsync(cfg)
|
||||||
|
|
||||||
|
private def parkOnStartup(): Unit =
|
||||||
|
park(defaultServerUrl, "expert") match
|
||||||
|
case Some(id) => log.infof("Parked expert bot on %s as id %s", defaultServerUrl, id)
|
||||||
|
case None => log.warnf("Failed to park expert bot on %s", defaultServerUrl)
|
||||||
|
|
||||||
|
private def parkToken(serverUrl: String, difficulty: String): Option[String] =
|
||||||
|
System.getenv().asScala.get("TOURNAMENT_BOT_TOKEN").filter(_.nonEmpty)
|
||||||
|
.orElse(registerBot(serverUrl, difficulty).map(_._2))
|
||||||
|
|
||||||
|
private def park(serverUrl: String, difficulty: String): Option[String] =
|
||||||
|
parkToken(serverUrl, difficulty).flatMap { token =>
|
||||||
|
Try {
|
||||||
|
val body = s"""{"name":"${botName(difficulty)}"}"""
|
||||||
|
val response = client
|
||||||
|
.target(serverUrl)
|
||||||
|
.path("api")
|
||||||
|
.path("bots")
|
||||||
|
.request(MediaType.APPLICATION_JSON)
|
||||||
|
.header("Authorization", s"Bearer $token")
|
||||||
|
.post(Entity.entity(body, MediaType.APPLICATION_JSON))
|
||||||
|
if response.getStatus == 201 || response.getStatus == 200 then
|
||||||
|
val id = objectMapper.readTree(response.readEntity(classOf[String])).path("id").asText()
|
||||||
|
response.close()
|
||||||
|
log.infof("Parked official bot %s as id %s", botName(difficulty), id)
|
||||||
|
Option(id).filter(_.nonEmpty)
|
||||||
|
else { log.warnf("Parking bot %s returned status %d", botName(difficulty), response.getStatus); response.close(); None }
|
||||||
|
}.getOrElse(None)
|
||||||
|
}
|
||||||
|
|
||||||
def joinTournament(tournamentId: String, difficulty: String, serverUrl: String): Either[String, String] =
|
def joinTournament(tournamentId: String, difficulty: String, serverUrl: String): Either[String, String] =
|
||||||
registerBot(serverUrl, difficulty) match
|
registerBot(serverUrl, difficulty) match
|
||||||
case None => Left("Failed to register bot with tournament server")
|
case None => Left("Failed to register bot with tournament server")
|
||||||
@@ -65,10 +95,11 @@ class TournamentBotGamePlayer:
|
|||||||
thread.setDaemon(true)
|
thread.setDaemon(true)
|
||||||
thread.start()
|
thread.start()
|
||||||
|
|
||||||
|
private def botName(difficulty: String): String = s"NowChess ${difficulty.capitalize}"
|
||||||
|
|
||||||
private def registerBot(serverUrl: String, difficulty: String): Option[(String, String)] =
|
private def registerBot(serverUrl: String, difficulty: String): Option[(String, String)] =
|
||||||
Try {
|
Try {
|
||||||
val name = s"NowChess ${difficulty.capitalize}"
|
val body = s"""{"name":"${botName(difficulty)}","isBot":true}"""
|
||||||
val body = s"""{"name":"$name","isBot":true}"""
|
|
||||||
val response = client
|
val response = client
|
||||||
.target(serverUrl)
|
.target(serverUrl)
|
||||||
.path("api")
|
.path("api")
|
||||||
|
|||||||
Reference in New Issue
Block a user