Compare commits

...

2 Commits

Author SHA1 Message Date
TeamCity
266cbe7509 ci: bump version to v4.11.0 2025-12-10 10:47:18 +00:00
e8b31b1748 feat: FRO-2 Implement Login Component (#105)
Reviewed-on: #105
Reviewed-by: lq64 <lq@blackhole.local>
Co-authored-by: Janis <janis.e.20@gmx.de>
Co-committed-by: Janis <janis.e.20@gmx.de>
2025-12-10 11:43:51 +01:00
10 changed files with 47 additions and 39 deletions

1
.gitignore vendored
View File

@@ -134,6 +134,7 @@ target
/.project
/.settings
/RUNNING_PID
/knockoutwhistwebfrontend/
/knockoutwhist/
/knockoutwhistweb/.g8/
/knockoutwhistweb/.bsp/

View File

@@ -229,3 +229,8 @@
### Features
* BAC-27 Implemented endpoint which returns information about the current state ([#103](https://git.janis-eccarius.de/KnockOutWhist/KnockOutWhist-Web/issues/103)) ([dd5e8e6](https://git.janis-eccarius.de/KnockOutWhist/KnockOutWhist-Web/commit/dd5e8e65e55f02a7618b3c60e8fc7087774e5106))
## (2025-12-10)
### Features
* FRO-2 Implement Login Component ([#105](https://git.janis-eccarius.de/KnockOutWhist/KnockOutWhist-Web/issues/105)) ([e8b31b1](https://git.janis-eccarius.de/KnockOutWhist/KnockOutWhist-Web/commit/e8b31b174819b5f033034501856c4b1189c4c4ee))

View File

@@ -23,12 +23,12 @@ class AuthAction @Inject()(val sessionManager: SessionManager, val parser: BodyP
case Some(user) =>
block(new AuthenticatedRequest(user, request))
case None =>
Future.successful(Results.Redirect(routes.UserController.login()))
Future.successful(Results.Unauthorized)
}
}
protected def getUserFromSession(request: RequestHeader): Option[User] = {
val session = request.cookies.get("sessionId")
val session = request.cookies.get("accessToken")
if (session.isDefined)
return sessionManager.getUserBySession(session.get.value)
None

View File

@@ -1,10 +1,13 @@
package controllers
import auth.{AuthAction, AuthenticatedRequest}
import dto.subDTO.UserDTO
import logic.user.{SessionManager, UserManager}
import model.users.User
import play.api.*
import play.api.libs.json.Json
import play.api.mvc.*
import play.api.mvc.Cookie.SameSite.{Lax, None, Strict}
import javax.inject.*
@@ -21,22 +24,6 @@ class UserController @Inject()(
val authAction: AuthAction
) extends BaseController {
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(routes.MainMenuController.mainMenu())
} else {
Ok(views.html.main("Login")(views.html.login.login()))
}
} else {
Ok(views.html.main("Login")(views.html.login.login()))
}
}
}
def login_Post(): Action[AnyContent] = {
Action { implicit request =>
val jsonBody = request.body.asJson
@@ -51,12 +38,17 @@ class UserController @Inject()(
val possibleUser = userManager.authenticate(username.get, password.get)
if (possibleUser.isDefined) {
Ok(Json.obj(
"status" -> "success",
"redirectUrl" -> routes.MainMenuController.mainMenu().url,
"content" -> views.html.mainmenu.creategame(possibleUser).toString
)).withCookies(
Cookie("sessionId", sessionManager.createSession(possibleUser.get))
)
"user" -> Json.obj(
"id" -> possibleUser.get.id,
"username" -> possibleUser.get.name
)
)).withCookies(Cookie(
name = "accessToken",
value = sessionManager.createSession(possibleUser.get),
httpOnly = true,
secure = false,
sameSite = Some(Lax)
))
} else {
Unauthorized("Invalid username or password")
}
@@ -65,14 +57,21 @@ class UserController @Inject()(
}
}
}
def getUserInfo(): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
val user: User = request.user
Ok(Json.obj(
"id" -> user.id,
"username" -> user.name
))
}
// 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")
def logoutPost(): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
val sessionCookie = request.cookies.get("accessToken")
if (sessionCookie.isDefined) {
sessionManager.invalidateSession(sessionCookie.get.value)
}
Redirect(routes.UserController.login()).discardingCookies(DiscardingCookie("sessionId"))
NoContent.discardingCookies(DiscardingCookie("accessToken"))
}
}

View File

@@ -9,6 +9,7 @@ trait SessionManager {
def createSession(user: User): String
def getUserBySession(sessionId: String): Option[User]
def invalidateSession(sessionId: String): Unit

View File

@@ -45,15 +45,9 @@
<li><a class="dropdown-item disabled" href="#" tabindex="-1" aria-disabled="true">
Settings</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="@routes.UserController.logout()">Logout</a></li>
</ul>
</li>
</ul>
} else {
<div class="d-flex ms-auto">
<a class="btn btn-outline-primary me-2" href="@routes.UserController.login()">Login</a>
<a class="btn btn-primary" href="@routes.UserController.login()">Sign Up</a>
</div>
}
</div>

View File

@@ -13,3 +13,12 @@ auth {
publicKeyFile = ${?PUBLIC_KEY_FILE}
publicKeyPem = ${?PUBLIC_KEY_PEM}
}
play.filters.enabled += "play.filters.cors.CORSFilter"
play.filters.cors {
allowedOrigins = ["http://localhost:5173"]
allowedCredentials = true
allowedHttpMethods = ["GET", "POST", "PUT", "DELETE", "OPTIONS"]
allowedHttpHeaders = ["Accept", "Content-Type", "Origin", "X-Requested-With"]
}

View File

@@ -18,10 +18,9 @@ POST /createGame controllers.MainMenuController.createGame()
POST /joinGame controllers.MainMenuController.joinGame()
# User authentication routes
GET /login controllers.UserController.login()
POST /login controllers.UserController.login_Post()
GET /logout controllers.UserController.logout()
POST /logout controllers.UserController.logoutPost()
GET /userInfo controllers.UserController.getUserInfo()
# In-game routes
GET /game/:id controllers.IngameController.game(id: String)

View File

@@ -1,3 +1,3 @@
MAJOR=4
MINOR=10
MINOR=11
PATCH=0