feat(user-sessions): implement user login, logout, and session management
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
package controllers
|
||||
|
||||
import com.google.inject.{Guice, Injector}
|
||||
import controllers.sessions.AdvancedSession
|
||||
import de.knockoutwhist.KnockOutWhist
|
||||
import de.knockoutwhist.components.Configuration
|
||||
import de.knockoutwhist.control.GameState.{InGame, Lobby, SelectTrump, TieBreak}
|
||||
|
||||
75
knockoutwhistweb/app/controllers/UserController.scala
Normal file
75
knockoutwhistweb/app/controllers/UserController.scala
Normal file
@@ -0,0 +1,75 @@
|
||||
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.PodGameManager
|
||||
import logic.user.{SessionManager, UserManager}
|
||||
import model.sessions.AdvancedSession
|
||||
import play.api.*
|
||||
import play.api.mvc.*
|
||||
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 UserController @Inject()(val controllerComponents: ControllerComponents, val sessionManager: SessionManager, val userManager: UserManager) 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("/main-menu")
|
||||
} 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 {
|
||||
Unauthorized("Invalid username or password")
|
||||
}
|
||||
} else {
|
||||
BadRequest("Invalid form submission")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def logout(): Action[AnyContent] = {
|
||||
Action { implicit request =>
|
||||
val sessionCookie = request.cookies.get("sessionId")
|
||||
if (sessionCookie.isDefined) {
|
||||
sessionManager.invalidateSession(sessionCookie.get.value)
|
||||
}
|
||||
NoContent.discardingCookies(DiscardingCookie("sessionId"))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
package logic.user
|
||||
|
||||
import com.google.inject.ImplementedBy
|
||||
import logic.user.impl.BaseSessionManager
|
||||
import model.users.User
|
||||
|
||||
@ImplementedBy(classOf[BaseSessionManager])
|
||||
trait SessionManager {
|
||||
|
||||
def createSession(user: User): String
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
package logic.user
|
||||
|
||||
import com.google.inject.ImplementedBy
|
||||
import logic.user.impl.StubUserManager
|
||||
import model.users.User
|
||||
|
||||
@ImplementedBy(classOf[StubUserManager])
|
||||
trait UserManager {
|
||||
|
||||
def addUser(name: String, password: String): Boolean
|
||||
def authenticate(name: String, password: String): Boolean
|
||||
def userExists(name: String): Boolean
|
||||
def authenticate(name: String, password: String): Option[User]
|
||||
def userExists(name: String): Option[User]
|
||||
def removeUser(name: String): Boolean
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package logic.user.impl
|
||||
|
||||
import com.typesafe.config.Config
|
||||
import logic.user.SessionManager
|
||||
import model.users.User
|
||||
|
||||
import javax.inject.{Inject, Singleton}
|
||||
|
||||
@Singleton
|
||||
class BaseSessionManager @Inject()(val config: Config) extends SessionManager {
|
||||
|
||||
override def createSession(user: User): String = {
|
||||
//TODO create JWT token instead of random string
|
||||
//Write session identifier to cache and DB
|
||||
val sessionId = java.util.UUID.randomUUID().toString
|
||||
sessionId
|
||||
}
|
||||
|
||||
override def getUserBySession(sessionId: String): Option[User] = {
|
||||
//TODO verify JWT token instead of looking up in cache
|
||||
//Read session identifier from cache and DB
|
||||
None
|
||||
}
|
||||
|
||||
override def invalidateSession(sessionId: String): Unit = {
|
||||
|
||||
}
|
||||
}
|
||||
47
knockoutwhistweb/app/logic/user/impl/StubUserManager.scala
Normal file
47
knockoutwhistweb/app/logic/user/impl/StubUserManager.scala
Normal file
@@ -0,0 +1,47 @@
|
||||
package logic.user.impl
|
||||
|
||||
import com.typesafe.config.Config
|
||||
import logic.user.UserManager
|
||||
import model.users.User
|
||||
import util.UserHash
|
||||
|
||||
import javax.inject.{Inject, Singleton}
|
||||
|
||||
@Singleton
|
||||
class StubUserManager @Inject()(val config: Config) extends UserManager {
|
||||
|
||||
private val user: Map[String, User] = Map(
|
||||
"Janis" -> User(
|
||||
internalId = 1L,
|
||||
id = java.util.UUID.fromString("123e4567-e89b-12d3-a456-426614174000"),
|
||||
name = "Janis",
|
||||
passwordHash = UserHash.hashPW("password123")
|
||||
),
|
||||
"Leon" -> User(
|
||||
internalId = 2L,
|
||||
id = java.util.UUID.fromString("223e4567-e89b-12d3-a456-426614174000"),
|
||||
name = "Leon",
|
||||
passwordHash = UserHash.hashPW("password123")
|
||||
)
|
||||
)
|
||||
|
||||
override def addUser(name: String, password: String): Boolean = {
|
||||
throw new NotImplementedError("StubUserManager.addUser is not implemented")
|
||||
}
|
||||
|
||||
override def authenticate(name: String, password: String): Option[User] = {
|
||||
user.get(name) match {
|
||||
case Some(u) if UserHash.verifyUser(password, u) => Some(u)
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
|
||||
override def userExists(name: String): Option[User] = {
|
||||
user.get(name)
|
||||
}
|
||||
|
||||
override def removeUser(name: String): Boolean = {
|
||||
throw new NotImplementedError("StubUserManager.removeUser is not implemented")
|
||||
}
|
||||
|
||||
}
|
||||
18
knockoutwhistweb/app/views/login.scala.html
Normal file
18
knockoutwhistweb/app/views/login.scala.html
Normal file
@@ -0,0 +1,18 @@
|
||||
@()
|
||||
|
||||
@main("Login") {
|
||||
<div class="container">
|
||||
<h2>Login</h2>
|
||||
<form action="@routes.UserController.login_Post()" method="post">
|
||||
<div class="form-group">
|
||||
<label for="username">Username:</label>
|
||||
<input type="text" class="form-control" id="username" name="username" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">Password:</label>
|
||||
<input type="password" class="form-control" id="password" name="password" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Login</button>
|
||||
</form>
|
||||
</div>
|
||||
}
|
||||
Reference in New Issue
Block a user