feat!: implemented multigame support #34
25
bruno/KnockOutWhist/Login.bru
Normal file
25
bruno/KnockOutWhist/Login.bru
Normal file
@@ -0,0 +1,25 @@
|
||||
meta {
|
||||
name: Login
|
||||
type: http
|
||||
seq: 1
|
||||
}
|
||||
|
||||
post {
|
||||
url: {{host}}/login
|
||||
body: formUrlEncoded
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
body:form-urlencoded {
|
||||
username: Janis
|
||||
password: password123
|
||||
}
|
||||
|
||||
body:multipart-form {
|
||||
username: Janis
|
||||
password: password123
|
||||
}
|
||||
|
||||
settings {
|
||||
encodeUrl: true
|
||||
}
|
||||
9
bruno/KnockOutWhist/bruno.json
Normal file
9
bruno/KnockOutWhist/bruno.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"version": "1",
|
||||
"name": "KnockOutWhist",
|
||||
"type": "collection",
|
||||
"ignore": [
|
||||
"node_modules",
|
||||
".git"
|
||||
]
|
||||
}
|
||||
3
bruno/KnockOutWhist/environments/Local.bru
Normal file
3
bruno/KnockOutWhist/environments/Local.bru
Normal file
@@ -0,0 +1,3 @@
|
||||
vars {
|
||||
host: http://localhost:9000
|
||||
}
|
||||
@@ -39,7 +39,7 @@ lazy val knockoutwhistweb = project.in(file("knockoutwhistweb"))
|
||||
commonSettings,
|
||||
libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "7.0.2" % Test,
|
||||
libraryDependencies += "de.mkammerer" % "argon2-jvm" % "2.12",
|
||||
// libraryDependencies += "com.auth0" % "java-jwt" % "4.5.0",
|
||||
libraryDependencies += "com.auth0" % "java-jwt" % "4.3.0",
|
||||
libraryDependencies += "com.github.ben-manes.caffeine" % "caffeine" % "3.2.2"
|
||||
)
|
||||
|
||||
|
||||
@@ -24,13 +24,31 @@ import javax.inject.*
|
||||
@Singleton
|
||||
class UserController @Inject()(val controllerComponents: ControllerComponents, val sessionManager: SessionManager, val userManager: UserManager) extends BaseController {
|
||||
|
||||
def mainMenu() : Action[AnyContent] = {
|
||||
Action { implicit request =>
|
||||
val session = request.cookies.get("sessionId")
|
||||
if (session.isDefined) {
|
||||
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] = {
|
||||
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")
|
||||
Redirect("/mainmenu")
|
||||
} else
|
||||
{
|
||||
Ok(views.html.login())
|
||||
@@ -54,6 +72,7 @@ class UserController @Inject()(val controllerComponents: ControllerComponents, v
|
||||
Cookie("sessionId", sessionManager.createSession(possibleUser.get))
|
||||
)
|
||||
} else {
|
||||
println("Failed login attempt for user: " + username)
|
||||
Unauthorized("Invalid username or password")
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -1,18 +1,27 @@
|
||||
package logic.user.impl
|
||||
|
||||
import com.auth0.jwt.JWT
|
||||
import com.auth0.jwt.algorithms.Algorithm
|
||||
import com.typesafe.config.Config
|
||||
import logic.user.SessionManager
|
||||
import model.users.User
|
||||
import services.JwtKeyProvider
|
||||
|
||||
import javax.inject.{Inject, Singleton}
|
||||
|
||||
@Singleton
|
||||
class BaseSessionManager @Inject()(val config: Config) extends SessionManager {
|
||||
class BaseSessionManager @Inject()(val keyProvider: JwtKeyProvider, val config: Config) extends SessionManager {
|
||||
|
||||
private val algorithm = Algorithm.RSA512(keyProvider.publicKey, keyProvider.privateKey)
|
||||
|
||||
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
|
||||
val sessionId = JWT.create()
|
||||
.withIssuer(config.getString("auth.issuer"))
|
||||
.withAudience(config.getString("auth.audience"))
|
||||
.withSubject(user.internalId.toString)
|
||||
.sign(algorithm)
|
||||
//TODO write to DB
|
||||
sessionId
|
||||
}
|
||||
|
||||
|
||||
56
knockoutwhistweb/app/services/JwtKeyProvider.scala
Normal file
56
knockoutwhistweb/app/services/JwtKeyProvider.scala
Normal file
@@ -0,0 +1,56 @@
|
||||
package services
|
||||
|
||||
import java.nio.file.{Files, Paths}
|
||||
import java.security.{KeyFactory, PrivateKey, PublicKey}
|
||||
import java.security.spec.X509EncodedKeySpec
|
||||
import java.util.Base64
|
||||
import javax.inject.*
|
||||
import play.api.Configuration
|
||||
|
||||
import java.security.interfaces.{RSAPrivateKey, RSAPublicKey}
|
||||
|
||||
@Singleton
|
||||
class JwtKeyProvider @Inject()(config: Configuration) {
|
||||
|
||||
private def cleanPem(pem: String): String =
|
||||
pem.replaceAll("-----BEGIN (.*)-----", "")
|
||||
.replaceAll("-----END (.*)-----", "")
|
||||
.replaceAll("\\s", "")
|
||||
|
||||
private def loadPublicKeyFromPem(pem: String): RSAPublicKey = {
|
||||
val decoded = Base64.getDecoder.decode(cleanPem(pem))
|
||||
val spec = new X509EncodedKeySpec(decoded)
|
||||
KeyFactory.getInstance("RSA").generatePublic(spec).asInstanceOf[RSAPublicKey]
|
||||
}
|
||||
|
||||
private def loadPrivateKeyFromPem(pem: String): RSAPrivateKey = {
|
||||
val decoded = Base64.getDecoder.decode(cleanPem(pem))
|
||||
val spec = new X509EncodedKeySpec(decoded)
|
||||
KeyFactory.getInstance("RSA").generatePrivate(spec).asInstanceOf[RSAPrivateKey]
|
||||
}
|
||||
|
||||
val publicKey: RSAPublicKey = {
|
||||
val pemOpt = config.getOptional[String]("auth.publicKeyPem")
|
||||
val fileOpt = config.getOptional[String]("auth.publicKeyFile")
|
||||
|
||||
pemOpt.orElse(fileOpt.map { path =>
|
||||
new String(Files.readAllBytes(Paths.get(path)))
|
||||
}) match {
|
||||
case Some(pem) => loadPublicKeyFromPem(pem)
|
||||
case None => throw new RuntimeException("No RSA public key configured.")
|
||||
}
|
||||
}
|
||||
|
||||
val privateKey: RSAPrivateKey = {
|
||||
val pemOpt = config.getOptional[String]("auth.privateKeyPem")
|
||||
val fileOpt = config.getOptional[String]("auth.privateKeyFile")
|
||||
|
||||
pemOpt.orElse(fileOpt.map { path =>
|
||||
new String(Files.readAllBytes(Paths.get(path)))
|
||||
}) match {
|
||||
case Some(pem) => loadPrivateKeyFromPem(pem)
|
||||
case None => throw new RuntimeException("No RSA private key configured.")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1 +1,12 @@
|
||||
# https://www.playframework.com/documentation/latest/Configuration
|
||||
play.filters.disabled += play.filters.csrf.CSRFFilter
|
||||
|
||||
|
||||
auth {
|
||||
issuer = "knockoutwhistweb"
|
||||
audience = "ui"
|
||||
privateKeyFile = D:\Workspaces\Gitops\rsa512-private.pem # ${?PUBLIC_KEY_FILE}
|
||||
privateKeyPem = ${?PUBLIC_KEY_PEM}
|
||||
publicKeyFile = D:\Workspaces\Gitops\rsa512-public.pem #${?PUBLIC_KEY_FILE}
|
||||
publicKeyPem = ${?PUBLIC_KEY_PEM}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ GET /assets/*file controllers.Assets.versioned(path="/public",
|
||||
|
||||
GET /rules controllers.HomeController.rules()
|
||||
|
||||
|
||||
GET /mainmenu controllers.UserController.mainMenu()
|
||||
GET /login controllers.UserController.login()
|
||||
POST /login controllers.UserController.login_Post()
|
||||
GET /logout controllers.UserController.logout()
|
||||
Reference in New Issue
Block a user