chore: rebase

This commit is contained in:
2025-11-01 11:44:55 +01:00
parent 8df3491757
commit b604e6935f
11 changed files with 185 additions and 160 deletions

View File

@@ -0,0 +1,39 @@
package auth
import controllers.routes
import logic.user.SessionManager
import model.users.User
import play.api.mvc.*
import javax.inject.Inject
import scala.concurrent.{ExecutionContext, Future}
class AuthenticatedRequest[A](val user: User, request: Request[A]) extends WrappedRequest[A](request)
class AuthAction @Inject()(val sessionManager: SessionManager, val parser: BodyParsers.Default)(implicit ec: ExecutionContext)
extends ActionBuilder[AuthenticatedRequest, AnyContent] {
override def executionContext: ExecutionContext = ec
// This simulates checking if a user is logged in (e.g. via session)
private def getUserFromSession(request: RequestHeader): Option[User] = {
val session = request.cookies.get("sessionId")
if (session.isDefined)
return sessionManager.getUserBySession(session.get.value)
None
}
// Transform a normal request into an AuthenticatedRequest
override def invokeBlock[A](
request: Request[A],
block: AuthenticatedRequest[A] => Future[Result]
): Future[Result] = {
getUserFromSession(request) match {
case Some(user) =>
block(new AuthenticatedRequest(user, request))
case None =>
Future.successful(Results.Redirect(routes.UserController.login()))
}
}
}

View File

@@ -1,89 +0,0 @@
package controllers
import com.google.inject.{Guice, Injector}
import de.knockoutwhist.KnockOutWhist
import de.knockoutwhist.components.Configuration
import de.knockoutwhist.control.GameState.{InGame, Lobby, SelectTrump, TieBreak}
import de.knockoutwhist.control.controllerBaseImpl.BaseGameLogic
import di.KnockOutWebConfigurationModule
import logic.PodManager
import model.sessions.SimpleSession
import play.api.mvc.*
import play.api.*
import play.twirl.api.Html
import java.util.UUID
import javax.inject.*
/**
* This controller creates an `Action` to handle HTTP requests to the
* application's home page.
*/
@Singleton
class HomeController @Inject()(val controllerComponents: ControllerComponents) extends BaseController {
private var initial = false
private val injector: Injector = Guice.createInjector(KnockOutWebConfigurationModule())
/**
* Create an Action to render an HTML page.
*
* The configuration in the `routes` file means that this method
* will be called when the application receives a `GET` request with
* a path of `/`.
*/
def index(): Action[AnyContent] = {
if (!initial) {
initial = true
KnockOutWhist.entry(injector.getInstance(classOf[Configuration]))
}
Action { implicit request =>
Redirect("/sessions")
}
}
def rules(): Action[AnyContent] = {
Action { implicit request =>
Ok(views.html.rules())
}
}
def sessions(): Action[AnyContent] = {
Action { implicit request =>
Ok(views.html.rules())
}
}
def ingame(id: String): Action[AnyContent] = {
val uuid: UUID = UUID.fromString(id)
Action { implicit request =>
NotFound(views.html.tui.apply(List(Html(s"<p>Session with id $id not found!</p>"))))
}
// if (PodGameManager.identify(uuid).isEmpty) {
// return Action { implicit request =>
// NotFound(views.html.tui.apply(List(Html(s"<p>Session with id $id not found!</p>"))))
// }
// } else {
// val session = PodGameManager.identify(uuid).get
// val player = session.asInstanceOf[SimpleSession].player
// val logic = BaseGameLogic(null)
// if (logic.getCurrentState == Lobby) {
//
// } else if (logic.getCurrentState == InGame) {
// return Action { implicit request =>
// Ok(views.html.ingame.apply(player, logic))
// }
// } else if (logic.getCurrentState == SelectTrump) {
// return Action { implicit request =>
// Ok(views.html.selecttrump.apply(player, logic))
// }
// } else if (logic.getCurrentState == TieBreak) {
// return Action { implicit request =>
// Ok(views.html.tie.apply(player, logic))
// }
// }
// }
// Action { implicit request =>
// InternalServerError("Oops")
// }
}
}

View File

@@ -0,0 +1,75 @@
package controllers
import auth.{AuthAction, AuthenticatedRequest}
import logic.user.{SessionManager, UserManager}
import play.api.*
import play.api.mvc.*
import javax.inject.*
/**
* This controller creates an `Action` to handle HTTP requests to the
* application's home page.
*/
@Singleton
class IngameController @Inject()(
val controllerComponents: ControllerComponents,
val sessionManager: SessionManager,
val userManager: UserManager,
val authAction: AuthAction
) extends BaseController {
// Pass the request-handling function directly to authAction (no nested Action)
def mainMenu(): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
Ok("Main Menu for user: " + request.user.name)
}
def login(): Action[AnyContent] = {
Action { implicit request =>
val session = request.cookies.get("sessionId")
if (session.isDefined) {
val possibleUser = sessionManager.getUserBySession(session.get.value)
if (possibleUser.isDefined) {
Redirect("/mainmenu")
} else {
Ok(views.html.login())
}
} else {
Ok(views.html.login())
}
}
}
def login_Post(): Action[AnyContent] = {
Action { implicit request =>
val postData = request.body.asFormUrlEncoded
if (postData.isDefined) {
// Extract username and password from form data
val username = postData.get.get("username").flatMap(_.headOption).getOrElse("")
val password = postData.get.get("password").flatMap(_.headOption).getOrElse("")
val possibleUser = userManager.authenticate(username, password)
if (possibleUser.isDefined) {
Redirect("/mainmenu").withCookies(
Cookie("sessionId", sessionManager.createSession(possibleUser.get))
)
} else {
println("Failed login attempt for user: " + username)
Unauthorized("Invalid username or password")
}
} else {
BadRequest("Invalid form submission")
}
}
}
// Pass the request-handling function directly to authAction (no nested Action)
def logout(): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
val sessionCookie = request.cookies.get("sessionId")
if (sessionCookie.isDefined) {
sessionManager.invalidateSession(sessionCookie.get.value)
}
NoContent.discardingCookies(DiscardingCookie("sessionId"))
}
}

View File

@@ -0,0 +1,35 @@
package controllers
import auth.{AuthAction, AuthenticatedRequest}
import play.api.*
import play.api.mvc.*
import javax.inject.*
/**
* This controller creates an `Action` to handle HTTP requests to the
* application's home page.
*/
@Singleton
class MainMenuController @Inject()(
val controllerComponents: ControllerComponents,
val authAction: AuthAction
) extends BaseController {
// Pass the request-handling function directly to authAction (no nested Action)
def mainMenu(): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
Ok("Main Menu for user: " + request.user.name)
}
def index(): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
Redirect("/mainmenu")
}
def rules(): Action[AnyContent] = {
Action { implicit request =>
Ok(views.html.rules())
}
}
}

View File

@@ -1,19 +1,10 @@
package controllers package controllers
import com.google.inject.{Guice, Injector} import auth.{AuthAction, AuthenticatedRequest}
import de.knockoutwhist.KnockOutWhist
import de.knockoutwhist.components.Configuration
import de.knockoutwhist.control.GameState.{InGame, Lobby, SelectTrump, TieBreak}
import de.knockoutwhist.control.controllerBaseImpl.BaseGameLogic
import di.KnockOutWebConfigurationModule
import logic.PodManager
import logic.user.{SessionManager, UserManager} import logic.user.{SessionManager, UserManager}
import model.sessions.SimpleSession
import play.api.* import play.api.*
import play.api.mvc.* import play.api.mvc.*
import play.twirl.api.Html
import java.util.UUID
import javax.inject.* import javax.inject.*
@@ -22,25 +13,12 @@ import javax.inject.*
* application's home page. * application's home page.
*/ */
@Singleton @Singleton
class UserController @Inject()(val controllerComponents: ControllerComponents, val sessionManager: SessionManager, val userManager: UserManager) extends BaseController { class UserController @Inject()(
val controllerComponents: ControllerComponents,
def mainMenu() : Action[AnyContent] = { val sessionManager: SessionManager,
Action { implicit request => val userManager: UserManager,
val session = request.cookies.get("sessionId") val authAction: AuthAction
if (session.isDefined) { ) extends BaseController {
val possibleUser = sessionManager.getUserBySession(session.get.value)
if (possibleUser.isDefined) {
Ok("Main Menu for user: " + possibleUser.get.name)
} else
{
println("Invalid session, redirecting to login")
Redirect("/login")
}
} else {
Redirect("/login")
}
}
}
def login(): Action[AnyContent] = { def login(): Action[AnyContent] = {
Action { implicit request => Action { implicit request =>
@@ -48,9 +26,8 @@ class UserController @Inject()(val controllerComponents: ControllerComponents, v
if (session.isDefined) { if (session.isDefined) {
val possibleUser = sessionManager.getUserBySession(session.get.value) val possibleUser = sessionManager.getUserBySession(session.get.value)
if (possibleUser.isDefined) { if (possibleUser.isDefined) {
Redirect("/mainmenu") Redirect(routes.MainMenuController.mainMenu())
} else } else {
{
Ok(views.html.login()) Ok(views.html.login())
} }
} else { } else {
@@ -68,7 +45,7 @@ class UserController @Inject()(val controllerComponents: ControllerComponents, v
val password = postData.get.get("password").flatMap(_.headOption).getOrElse("") val password = postData.get.get("password").flatMap(_.headOption).getOrElse("")
val possibleUser = userManager.authenticate(username, password) val possibleUser = userManager.authenticate(username, password)
if (possibleUser.isDefined) { if (possibleUser.isDefined) {
Redirect("/mainmenu").withCookies( Redirect(routes.MainMenuController.mainMenu()).withCookies(
Cookie("sessionId", sessionManager.createSession(possibleUser.get)) Cookie("sessionId", sessionManager.createSession(possibleUser.get))
) )
} else { } else {
@@ -81,14 +58,13 @@ class UserController @Inject()(val controllerComponents: ControllerComponents, v
} }
} }
def logout(): Action[AnyContent] = { // Pass the request-handling function directly to authAction (no nested Action)
Action { implicit request => def logout(): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
val sessionCookie = request.cookies.get("sessionId") val sessionCookie = request.cookies.get("sessionId")
if (sessionCookie.isDefined) { if (sessionCookie.isDefined) {
sessionManager.invalidateSession(sessionCookie.get.value) sessionManager.invalidateSession(sessionCookie.get.value)
} }
NoContent.discardingCookies(DiscardingCookie("sessionId")) NoContent.discardingCookies(DiscardingCookie("sessionId"))
} }
}
} }

View File

@@ -3,14 +3,14 @@ package logic.game
import de.knockoutwhist.cards.{Hand, Suit} import de.knockoutwhist.cards.{Hand, Suit}
import de.knockoutwhist.control.GameLogic import de.knockoutwhist.control.GameLogic
import de.knockoutwhist.control.GameState.Lobby import de.knockoutwhist.control.GameState.Lobby
import de.knockoutwhist.control.controllerBaseImpl.sublogic.util.{MatchUtil, PlayerUtil, RoundUtil} import de.knockoutwhist.control.controllerBaseImpl.sublogic.util.{MatchUtil, PlayerUtil}
import de.knockoutwhist.events.global.SessionClosed import de.knockoutwhist.events.global.SessionClosed
import de.knockoutwhist.events.player.PlayerEvent import de.knockoutwhist.events.player.PlayerEvent
import de.knockoutwhist.player.Playertype.HUMAN import de.knockoutwhist.player.Playertype.HUMAN
import de.knockoutwhist.player.{AbstractPlayer, PlayerFactory} import de.knockoutwhist.player.{AbstractPlayer, PlayerFactory}
import de.knockoutwhist.rounds.{Match, Round, Trick} import de.knockoutwhist.rounds.{Match, Round, Trick}
import de.knockoutwhist.utils.events.{EventListener, SimpleEvent} import de.knockoutwhist.utils.events.{EventListener, SimpleEvent}
import exceptions.{CantPlayCardException, GameFullException, NotHostException, NotInThisGameException, NotInteractableException} import exceptions.*
import model.sessions.{InteractionType, UserSession} import model.sessions.{InteractionType, UserSession}
import model.users.User import model.users.User

View File

@@ -1,7 +1,7 @@
package logic.user.impl package logic.user.impl
import com.auth0.jwt.{JWT, JWTVerifier}
import com.auth0.jwt.algorithms.Algorithm import com.auth0.jwt.algorithms.Algorithm
import com.auth0.jwt.{JWT, JWTVerifier}
import com.github.benmanes.caffeine.cache.{Cache, Caffeine} import com.github.benmanes.caffeine.cache.{Cache, Caffeine}
import com.typesafe.config.Config import com.typesafe.config.Config
import logic.user.SessionManager import logic.user.SessionManager

View File

@@ -1,13 +1,13 @@
package services package services
import java.nio.file.{Files, Paths}
import java.security.{KeyFactory, KeyPair, PrivateKey, PublicKey}
import java.security.spec.{PKCS8EncodedKeySpec, RSAPublicKeySpec, X509EncodedKeySpec}
import java.util.Base64
import javax.inject.*
import play.api.Configuration import play.api.Configuration
import java.nio.file.{Files, Paths}
import java.security.interfaces.{RSAPrivateKey, RSAPublicKey} import java.security.interfaces.{RSAPrivateKey, RSAPublicKey}
import java.security.spec.{PKCS8EncodedKeySpec, RSAPublicKeySpec, X509EncodedKeySpec}
import java.security.{KeyFactory, KeyPair, PrivateKey, PublicKey}
import java.util.Base64
import javax.inject.*
@Singleton @Singleton
class JwtKeyProvider @Inject()(config: Configuration) { class JwtKeyProvider @Inject()(config: Configuration) {

View File

@@ -1,13 +0,0 @@
@import model.sessions.PlayerSession
@(sessions: List[PlayerSession])
@main("Sessions") {
<div id="sessions" class="game-field-background">
<h1>Knockout Whist sessions</h1>
<p id="textanimation">Please select your session to jump inside the game!</p>
@for(session <- sessions) {
<a id="textanimation" href="@routes.HomeController.ingame(session.id.toString)">@session.name</a><br>
}
</div>
}

View File

@@ -6,9 +6,9 @@ auth {
issuer = "knockoutwhistweb" issuer = "knockoutwhistweb"
audience = "ui" audience = "ui"
# ${?PUBLIC_KEY_FILE} # ${?PUBLIC_KEY_FILE}
privateKeyFile = "/home/janis/Workspaces/IntelliJ/KnockOutWhist/Gitops/rsa512-private.pem" privateKeyFile = "D:\\Workspaces\\Gitops\\rsa512-private.pem"
privateKeyPem = ${?PUBLIC_KEY_PEM} privateKeyPem = ${?PUBLIC_KEY_PEM}
#${?PUBLIC_KEY_FILE} #${?PUBLIC_KEY_FILE}
publicKeyFile = "/home/janis/Workspaces/IntelliJ/KnockOutWhist/Gitops/rsa512-public.pem" publicKeyFile = "D:\\Workspaces\\Gitops\\rsa512-public.pem"
publicKeyPem = ${?PUBLIC_KEY_PEM} publicKeyPem = ${?PUBLIC_KEY_PEM}
} }

View File

@@ -3,17 +3,19 @@
# https://www.playframework.com/documentation/latest/ScalaRouting # https://www.playframework.com/documentation/latest/ScalaRouting
# ~~~~ # ~~~~
# An example controller showing a sample home page
GET / controllers.HomeController.index() # Primary routes
GET /sessions controllers.HomeController.sessions() GET / controllers.MainMenuController.index()
GET /ingame/:id controllers.HomeController.ingame(id: String)
# Map static resources from the /public folder to the /assets URL path
GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset) GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset)
GET /rules controllers.HomeController.rules() # Main menu routes
GET /mainmenu controllers.MainMenuController.mainMenu()
GET /rules controllers.MainMenuController.rules()
GET /mainmenu controllers.UserController.mainMenu() # User authentication routes
GET /login controllers.UserController.login() GET /login controllers.UserController.login()
POST /login controllers.UserController.login_Post() POST /login controllers.UserController.login_Post()
GET /logout controllers.UserController.logout() GET /logout controllers.UserController.logout()
# In-game routes
# GET /ingame/:id controllers.MainMenuController.ingame(id: String)