diff --git a/.gitignore b/.gitignore index 8113cf5..a2f3555 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,8 @@ graphify-out/ .DS_Store /jacoco-reporter/.venv/ /.claude/settings.local.json +modules/tournament/src/main/resources/keys/dev-public.pem +modules/account/src/main/resources/keys/dev-private.pem +modules/account/src/main/resources/keys/dev-public.pem +modules/core/src/main/resources/keys/dev-public.pem +java_pid2736.hprof diff --git a/modules/account/src/main/scala/de/nowchess/account/domain/UserAccount.scala b/modules/account/src/main/scala/de/nowchess/account/domain/UserAccount.scala index ebd16b0..85ae74c 100644 --- a/modules/account/src/main/scala/de/nowchess/account/domain/UserAccount.scala +++ b/modules/account/src/main/scala/de/nowchess/account/domain/UserAccount.scala @@ -51,7 +51,7 @@ class BotAccount extends PanacheEntityBase: @JoinColumn(name = "owner_id", nullable = false) var owner: UserAccount = uninitialized - @Column(unique = true, nullable = false, length = 256) + @Column(unique = true, nullable = false, length = 1024) var token: String = uninitialized var rating: Int = 1500 diff --git a/modules/account/src/main/scala/de/nowchess/account/service/AccountService.scala b/modules/account/src/main/scala/de/nowchess/account/service/AccountService.scala index b7d8569..114dfe2 100644 --- a/modules/account/src/main/scala/de/nowchess/account/service/AccountService.scala +++ b/modules/account/src/main/scala/de/nowchess/account/service/AccountService.scala @@ -153,9 +153,10 @@ class AccountService: val bot = new BotAccount() bot.name = botName bot.owner = owner - bot.token = generateBotToken(bot.id) + bot.token = UUID.randomUUID().toString bot.createdAt = Instant.now() botAccountRepository.persist(bot) + bot.token = generateBotToken(bot.id, bot.name) log.infof("Bot account %s created for owner %s", botName, ownerId.toString) Right(bot) @@ -194,7 +195,7 @@ class AccountService: case Some(bot) => if bot.owner.id != ownerId then Left(AccountError.NotAuthorized) else - bot.token = generateBotToken(botId) + bot.token = generateBotToken(botId, bot.name) botAccountRepository.persist(bot) Right(bot) @@ -217,12 +218,13 @@ class AccountService: officialBotAccountRepository.delete(botId) Right(()) - private def generateBotToken(botId: UUID): String = + private def generateBotToken(botId: UUID, botName: String): String = Jwt .issuer("nowchess") .subject(botId.toString) .expiresAt(Long.MaxValue) .claim("type", "bot") + .claim("name", botName) .sign() @Transactional diff --git a/modules/tournament/src/main/scala/de/nowchess/tournament/service/TournamentService.scala b/modules/tournament/src/main/scala/de/nowchess/tournament/service/TournamentService.scala index 3112214..8acbda0 100644 --- a/modules/tournament/src/main/scala/de/nowchess/tournament/service/TournamentService.scala +++ b/modules/tournament/src/main/scala/de/nowchess/tournament/service/TournamentService.scala @@ -140,7 +140,7 @@ class TournamentService: Some(CorePlayerInfo(white.botId, white.botName)), Some(CorePlayerInfo(black.botId, black.botName)), Some(tc), - if t.rated then Some("Rated") else Some("Casual"), + Some("Authenticated"), ) Try(coreGameClient.createGame(req)) match case Failure(ex) => log.errorf(ex, "Failed to create game for round %d in tournament %s", round, tournamentId) diff --git a/modules/ws/src/main/scala/de/nowchess/ws/resource/GameWebSocketResource.scala b/modules/ws/src/main/scala/de/nowchess/ws/resource/GameWebSocketResource.scala index 30f4c26..cb0126d 100644 --- a/modules/ws/src/main/scala/de/nowchess/ws/resource/GameWebSocketResource.scala +++ b/modules/ws/src/main/scala/de/nowchess/ws/resource/GameWebSocketResource.scala @@ -96,6 +96,8 @@ class GameWebSocketResource: private def resolvePlayerId(handshake: HandshakeRequest): Option[String] = Option(handshake.header("Authorization")) .filter(_.nonEmpty) + .map(h => if h.startsWith("Bearer ") then h.drop(7) else h) + .orElse(Option(handshake.query("token")).filter(_.nonEmpty)) .flatMap(token => Try(jwtParser.parse(token)).toOption) .map(_.getSubject) diff --git a/modules/ws/src/main/scala/de/nowchess/ws/resource/UserWebSocketResource.scala b/modules/ws/src/main/scala/de/nowchess/ws/resource/UserWebSocketResource.scala index 89434f5..fa4f07b 100644 --- a/modules/ws/src/main/scala/de/nowchess/ws/resource/UserWebSocketResource.scala +++ b/modules/ws/src/main/scala/de/nowchess/ws/resource/UserWebSocketResource.scala @@ -35,8 +35,7 @@ class UserWebSocketResource: @OnOpen def onOpen(connection: WebSocketConnection, handshake: HandshakeRequest): Unit = - val userIdOpt = Option(handshake.header("Authorization")) - .filter(_.nonEmpty) + val userIdOpt = resolveToken(handshake) .flatMap(token => Try(jwtParser.parse(token)).toOption) .map(_.getSubject) @@ -52,6 +51,12 @@ class UserWebSocketResource: val connectedMsg = s"""{"type":"CONNECTED","userId":"$userId"}""" connection.sendText(connectedMsg).subscribe().`with`(_ => (), _ => ()) + private def resolveToken(handshake: HandshakeRequest): Option[String] = + Option(handshake.header("Authorization")) + .filter(_.nonEmpty) + .map(h => if h.startsWith("Bearer ") then h.drop(7) else h) + .orElse(Option(handshake.query("token")).filter(_.nonEmpty)) + @OnClose def onClose(connection: WebSocketConnection): Unit = log.infof("User WebSocket closed — connectionId=%s", connection.id())