fixed some aspects in login page handler and working on register page handler
continuous-integration/drone/push Build is failing Details

drone-setup
Override-6 2 years ago
parent 47cb113c8d
commit d35a61212a

@ -1,6 +1,6 @@
database { database {
dataSourceClassName = org.sqlite.SQLiteDataSource dataSourceClassName = org.sqlite.SQLiteDataSource
dataSource { dataSource {
url = "jdbc:sqlite:database.sqlite;DB_CLOSE_DELAY=-1;INIT=RUNSCRIPT FROM 'classpath:table_init.sql'" url = "jdbc:sqlite:database.sqlite"
} }
} }

@ -4,7 +4,7 @@
<Appenders> <Appenders>
<Console name="Console" target="SYSTEM_OUT"> <Console name="Console" target="SYSTEM_OUT">
<PatternLayout disableAnsi="false" <PatternLayout disableAnsi="false"
pattern="%style{[%d{HH:mm:ss,SSS}]}{magenta} [%highlight{%-5p}{FATAL=red, ERROR=red, WARN=yellow, INFO=blue, DEBUG=green, TRACE=normal} _ %-6logger] %style{-}{normal} %highlight{%m%n}{FATAL=red, ERROR=red, WARN=yellow, INFO=blue, DEBUG=green, TRACE=normal}"/> pattern="%style{[%d{HH:mm:ss,SSS}]}{magenta} [%highlight{%-5p}{FATAL=red, ERROR=red, WARN=yellow, INFO=blue, DEBUG=green, TRACE=normal} _ %-30c{1.}] %style{-}{normal} %highlight{%m%n}{FATAL=red, ERROR=red, WARN=yellow, INFO=blue, DEBUG=green, TRACE=normal}"/>
</Console> </Console>
<RollingFile name="LogFile" <RollingFile name="LogFile"
fileName="log/server-current.log" fileName="log/server-current.log"
@ -19,6 +19,10 @@
</Appenders> </Appenders>
<Loggers> <Loggers>
<Logger level="INFO" name="com.zaxxer.hikari.pool.HikariPool">
</Logger>
<Logger name="Database" additivity="false" includeLocation="false"> <Logger name="Database" additivity="false" includeLocation="false">
<AppenderRef ref="Console"/> <AppenderRef ref="Console"/>
<AppenderRef ref="LogFile"/> <AppenderRef ref="LogFile"/>

@ -1,2 +0,0 @@
module core {
}

@ -4,22 +4,22 @@ import org.apache.logging.log4j.LogManager
import org.tbasket.auth.Authentificator import org.tbasket.auth.Authentificator
import org.tbasket.data.Database import org.tbasket.data.Database
import org.tbasket.endpoint.Endpoint import org.tbasket.endpoint.Endpoint
import pdi.jwt.algorithms.JwtAsymmetricAlgorithm
import zio.* import zio.*
import zio.http.URL import zio.http.URL
import pdi.jwt.algorithms.JwtAsymmetricAlgorithm
import java.lang import java.lang
import java.nio.file.{Files, Path} import java.nio.file.{Files, Path}
import java.security.PublicKey import java.security.PublicKey
import java.security.cert.CertificateFactory import java.security.cert.CertificateFactory
import java.security.spec.{KeySpec, PKCS8EncodedKeySpec, RSAPrivateKeySpec, X509EncodedKeySpec}
import java.util.Properties import java.util.Properties
import scala.io.StdIn import scala.io.StdIn
import scala.util.{Failure, Success}
import scala.util.control.NonFatal import scala.util.control.NonFatal
import java.security.spec.{KeySpec, PKCS8EncodedKeySpec, RSAPrivateKeySpec, X509EncodedKeySpec} import scala.util.{Failure, Success}
object Main extends ZIOAppDefault: object Main extends ZIOAppDefault:
final val LOG = LogManager.getLogger("Core") final val LOG = LogManager.getLogger("Core")
override def run = ZIO.serviceWithZIO[ZIOAppArgs] { args => override def run = ZIO.serviceWithZIO[ZIOAppArgs] { args =>
for for
@ -43,11 +43,11 @@ object Main extends ZIOAppDefault:
private def setupAuth(config: ServerConfig) = ZIO.attempt { private def setupAuth(config: ServerConfig) = ZIO.attempt {
val publicKey = config.emitterCertificate.getPublicKey val publicKey = config.emitterCertificate.getPublicKey
val auth = new Authentificator(config.emitterURL, publicKey, config.emitterCertificateAlgorithm) val auth = new Authentificator(config.emitterURL, publicKey, config.emitterCertificateAlgorithm)
ZLayer.succeed(auth) ZLayer.succeed(auth)
} }
private def retrieveConfig(args: ZIOAppArgs): Task[ServerConfig] = ZIO.attempt { private def retrieveConfig(args: ZIOAppArgs): Task[ServerConfig] = ZIO.attempt {
val configFile = Path.of("server.properties") val configFile = Path.of("server.properties")
if Files.notExists(configFile) then if Files.notExists(configFile) then

@ -7,8 +7,14 @@ import io.circe.syntax.*
import io.getquill.* import io.getquill.*
import io.getquill.context.qzio.ZioJdbcContext import io.getquill.context.qzio.ZioJdbcContext
import io.getquill.context.sql.idiom.SqlIdiom import io.getquill.context.sql.idiom.SqlIdiom
import org.apache.logging.log4j.LogManager
import org.tbasket.InternalBasketServerException import org.tbasket.InternalBasketServerException
import org.tbasket.data.User import org.tbasket.auth.Authentificator.LOG
import org.tbasket.data.{DatabaseContext, User}
import org.tbasket.error.AuthException.*
import org.tbasket.error.ExceptionEnum
import org.tbasket.error.JwtException.*
import org.tbasket.error.RegularException.*
import pdi.jwt.algorithms.JwtAsymmetricAlgorithm import pdi.jwt.algorithms.JwtAsymmetricAlgorithm
import pdi.jwt.{JwtClaim, JwtZIOJson} import pdi.jwt.{JwtClaim, JwtZIOJson}
import zio.* import zio.*
@ -24,14 +30,15 @@ import java.util.UUID
import javax.sql.DataSource import javax.sql.DataSource
import scala.collection.immutable.HashMap import scala.collection.immutable.HashMap
enum AuthentificatorError:
case ExpiredToken
case InvalidEmitterResponse
case class JwtContent(uuid: UUID) case class JwtContent(uuid: UUID)
object Authentificator:
private final val LOG = LogManager.getLogger("Authentification")
class Authentificator(url: URL, key: PublicKey, algorithm: JwtAsymmetricAlgorithm) { class Authentificator(url: URL, key: PublicKey, algorithm: JwtAsymmetricAlgorithm) {
private def defineCustomClaims(user: User): String = { private def defineCustomClaims(user: User): String = {
JwtContent(user.id).asJson.noSpaces.toString JwtContent(user.id).asJson.noSpaces.toString
} }
@ -41,7 +48,7 @@ class Authentificator(url: URL, key: PublicKey, algorithm: JwtAsymmetricAlgorith
Request(Body.fromString(custom), Headers.empty, Method.GET, url, Http_1_1, None) Request(Body.fromString(custom), Headers.empty, Method.GET, url, Http_1_1, None)
} }
def requestJwt(user: User) = { def requestNewJwt(user: User) = {
Client.request(mkRequest(user)) Client.request(mkRequest(user))
.flatMap { .flatMap {
case Response(Ok, _, body, _, _) => case Response(Ok, _, body, _, _) =>
@ -53,22 +60,68 @@ class Authentificator(url: URL, key: PublicKey, algorithm: JwtAsymmetricAlgorith
} }
} }
private def hashPassword(password: String) = {
password.hashCode //TODO user BCrypt or argon2id
}
private def findByMail(mail: String) = ZIO.serviceWithZIO[DatabaseContext] { ctx =>
import ctx.v.*
run(query[User].filter(_.mailAddress == lift(mail))).map(_.headOption)
}
def loginUser(mail: String, password: String) = ZIO.serviceWithZIO[DatabaseContext] { ctx =>
import ctx.v.*
findByMail(mail)
.someOrFail(UserNotFound)
.filterOrFail(_.passwordHash == hashPassword(password))(InvalidPassword)
}
private inline def insert(user: User) = quote {
query[User].insert(
_.id -> user.id,
_.name -> user.name,
_.forename -> user.forename,
_.passwordHash -> user.passwordHash,
_.mailAddress -> user.mailAddress,
)
}
def registerUser(name: String, forename: String, mail: String, password: String) = ZIO.serviceWithZIO[DatabaseContext] { ctx =>
import ctx.*
lazy val user = {
val uuid = UUID.randomUUID()
val hash = hashPassword(password)
User(uuid, name, forename, hash, mail)
}
for
_ <- findByMail(mail).none.orElse(ZIO.fail(UserAlreadyRegistered))
_ <- run(insert(user)).fork
yield user
}
def validateAndGetUser(jwt: String) = { def validateAndGetUser(jwt: String) = {
for for
//decoding token //decoding token
claims <- ZIO.fromTry(JwtZIOJson.decode(jwt, key, Seq(algorithm))) claims <-
ZIO.fromTry(JwtZIOJson.decode(jwt, key, Seq(algorithm)))
.mapError(InternalError.apply)
//ensure that the token is not expired (or else fail) //ensure that the token is not expired (or else fail)
_ <- ZIO.attempt(claims.expiration) _ <- ZIO.attempt(claims.expiration)
.someOrFail("Received invalid jwt token (missing expiration date)") .someOrElseZIO(ZIO.dieMessage("Invalid token"))
.filterOrFail(_ <= java.lang.System.currentTimeMillis())("Expired token") .filterOrFail(_ <= java.lang.System.currentTimeMillis())(ExpiredJwt)
// //
uuid <- ZIO.attempt(claims.content) uuid <- ZIO.attempt(claims.content)
.mapAttempt(decode[JwtContent](_)) .mapAttempt(decode[JwtContent](_))
.flatMap(ZIO.fromEither(_)) .flatMap(ZIO.fromEither(_))
.map(_.uuid) .map(_.uuid)
.mapError(InternalError.apply)
user <- ZIO.serviceWithZIO[ZioJdbcContext[SqlIdiom, NamingStrategy]] { ctx => user <- ZIO.serviceWithZIO[ZioJdbcContext[SqlIdiom, NamingStrategy]] { ctx =>
import ctx._ import ctx.*
run(quote { run(quote {
query[User].filter(_.id == lift(uuid)) query[User].filter(_.id == lift(uuid))
}).map(_.headOption) }).map(_.headOption)

@ -15,7 +15,7 @@ import javax.sql
class Database(config: ServerConfig): class Database(config: ServerConfig):
val contextLayer = ZLayer.succeed(new SqliteZioJdbcContext(SnakeCase)) val contextLayer = ZLayer.succeed(DatabaseContext(new SqliteZioJdbcContext(SnakeCase)))
val datasourceLayer = Quill.DataSource.fromPrefix("database") val datasourceLayer = Quill.DataSource.fromPrefix("database")

@ -0,0 +1,11 @@
package org.tbasket.data
import io.getquill.NamingStrategy
import io.getquill.context.qzio.ZioJdbcContext
import io.getquill.context.sql.idiom.SqlIdiom
case class DatabaseContext(v: ZioJdbcContext[SqlIdiom, NamingStrategy]):
export v.*

@ -4,10 +4,11 @@ import io.getquill.*
import java.util.UUID import java.util.UUID
case class User( final case class User(
id : UUID, id : UUID,
name : String, name : String,
forename : String, forename : String,
passwordHash: Int, passwordHash: Int,
mailAddress : String mailAddress : String
) )

@ -1,23 +1,24 @@
package org.tbasket.endpoint package org.tbasket.endpoint
import io.getquill.{Literal, NamingStrategy, SqliteDialect}
import io.getquill.context.qzio.{ZioContext, ZioJdbcContext} import io.getquill.context.qzio.{ZioContext, ZioJdbcContext}
import io.getquill.context.sql.idiom.SqlIdiom import io.getquill.context.sql.idiom.SqlIdiom
import io.getquill.idiom.Idiom
import io.getquill.{Literal, NamingStrategy, SqliteDialect}
import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.LogManager
import org.tbasket.auth.Authentificator import org.tbasket.auth.Authentificator
import org.tbasket.data.DatabaseContext
import org.tbasket.endpoint.Endpoint.LOG import org.tbasket.endpoint.Endpoint.LOG
import org.tbasket.handler.LoginHandler import org.tbasket.handler.LoginPageHandler
import zio.* import zio.*
import zio.http.* import zio.http.*
import zio.http.ServerConfig.LeakDetectionLevel import zio.http.ServerConfig.LeakDetectionLevel
import zio.http.model.Method.{GET, POST} import zio.http.model.Method.{GET, POST}
import zio.http.model.Status import zio.http.model.Status
import zio.http.model.Status.InternalServerError
import scala.collection.mutable
import zio.http.netty.client.ConnectionPool import zio.http.netty.client.ConnectionPool
import io.getquill.idiom.Idiom
import javax.sql.DataSource import javax.sql.DataSource
import scala.collection.mutable
class Endpoint(port: Int): class Endpoint(port: Int):
@ -26,10 +27,10 @@ class Endpoint(port: Int):
private def applyGenerics(response: Response): Response = private def applyGenerics(response: Response): Response =
response.withAccessControlAllowOrigin("*") response.withAccessControlAllowOrigin("*")
private val app = Http.collectZIO[Request] { private val app = Http.collectZIO[Request] {
case r@POST -> _ / "login" => case r@POST -> _ / "login" =>
LoginHandler.post(r) LoginPageHandler.post(r)
case r@method -> path => case r@method -> path =>
val ipInsights = r.remoteAddress val ipInsights = r.remoteAddress
.map(ip => s": request received from $ip.") .map(ip => s": request received from $ip.")
@ -38,7 +39,34 @@ class Endpoint(port: Int):
s"Was unable to find a handler for request '$path' with method $method ${ipInsights}" s"Was unable to find a handler for request '$path' with method $method ${ipInsights}"
) )
ZIO.succeed(Response(Status.NotFound)) ZIO.succeed(Response(Status.NotFound))
}.map(applyGenerics) }.catchAllCause(handleUnexpectedError)
.map(applyGenerics)
private def handleUnexpectedError(cause: Cause[Throwable]): HttpApp[Any, Throwable] = {
def report(kind: String, value: Throwable = null, trace: StackTrace = StackTrace.none) =
LOG.error(s"Received unhandled $kind cause ${if value == null then "" else ": " + value}")
LOG.error(trace)
cause match
case Cause.Empty => report("empty")
case Cause.Fail(e, trace) => report("failure", e, trace)
case Cause.Die(e, trace) => report("die", e, trace)
case Cause.Interrupt(fiberId, e) => report(s"interruption of $fiberId", null, e)
case Cause.Stackless(cause, _) =>
LOG.error("stackless error :")
handleUnexpectedError(cause)
case Cause.Then(left, right) =>
handleUnexpectedError(left)
LOG.error("**THEN this error occurred : **")
handleUnexpectedError(right)
case Cause.Both(left, right) =>
handleUnexpectedError(left)
LOG.error("**AND this error also occurred (async) : **")
handleUnexpectedError(right)
Http.succeed(Response.status(InternalServerError))
}
val run = val run =
val config = ServerConfig.default val config = ServerConfig.default
@ -49,7 +77,7 @@ class Endpoint(port: Int):
Server.install(app).flatMap { port => Server.install(app).flatMap { port =>
LOG.info(s"Listening API entries on $port") LOG.info(s"Listening API entries on $port")
ZIO.never ZIO.never
}.provideSome[ZioJdbcContext[SqlIdiom, NamingStrategy] & Authentificator & DataSource]( }.provideSome[DatabaseContext & Authentificator & DataSource](
Scope.default, Scope.default,
serverConfigLayer, serverConfigLayer,
ConnectionPool.fixed(4), ConnectionPool.fixed(4),

@ -0,0 +1,9 @@
package org.tbasket.error
import org.tbasket.error.ExceptionEnum
enum AuthException extends ExceptionEnum {
case InvalidPassword
case UserNotFound
case UserAlreadyRegistered
}

@ -0,0 +1,5 @@
package org.tbasket.error
trait ExceptionEnum extends Exception {
}

@ -0,0 +1,10 @@
package org.tbasket.error
enum JwtException extends ExceptionEnum {
case InvalidJwt(cause: String)
case ExpiredJwt
case InvalidEmitterResponse
case EmitterInternalError
}

@ -0,0 +1,9 @@
package org.tbasket.error
enum RegularException extends ExceptionEnum {
case InternalError(cause: Throwable)
case InvalidArgumentError(cause: String)
case InvalidRequest(msg: String, cause: String)
}

@ -1,11 +1,18 @@
package org.tbasket.handler package org.tbasket.handler
import zio.ZIO import org.tbasket.error.RegularException.InvalidRequest
import zio.{Task, ZIO}
import zio.http.Body import zio.http.Body
import zio.json.ast.Json import zio.json.*
import zio.json.ast.JsonCursor import zio.json.ast.{Json, JsonCursor}
import scala.language.reflectiveCalls
object HandlerUtils { object HandlerUtils {
def errorBody(errorType: String, msg: String) = Body.fromString(s"""{"error": $errorType,"msg": "$msg"}""") def parseAttribute[V, T <: Json {def value: V}](json: Json, name: String, cursor: JsonCursor[Json, T]): Task[V] =
ZIO.fromEither(json.get[T](cursor).map(_.value))
.mapError(InvalidRequest(s"Missing or invalid field $name.", _))
def errorBody(errorType: String, msg: String) = Body.fromString(s"""{"error": $errorType,"msg": "$msg"}""")
} }

@ -1,104 +0,0 @@
package org.tbasket.handler
import io.getquill.*
import io.getquill.context.ZioJdbc.*
import io.getquill.context.qzio.{ZioContext, ZioJdbcContext}
import io.getquill.context.sql.idiom.SqlIdiom
import org.tbasket.auth.Authentificator
import org.tbasket.handler.HandlerUtils.errorBody
import org.tbasket.handler.LoginError.*
import org.tbasket.data.User
import zio.http.*
import zio.http.model.{Cookie, Header, Headers, Status}
import zio.json.*
import zio.json.ast.Json.Str
import zio.json.ast.{Json, JsonCursor}
import zio.{ZEnvironment, ZIO, *}
import java.sql.SQLException
import java.util.UUID
enum LoginError:
case TokenNotFound(token: UUID)
case UserNotFound(user: User)
case InvalidPassword
case InvalidRequest(msg: String, cause: String)
case InternalError(t: Throwable)
object LoginHandler:
private def getUser(json: Json) =
ZIO.serviceWithZIO[ZioJdbcContext[SqlIdiom, NamingStrategy]] { ctx =>
import ctx.*
for
mail <-
ZIO.fromEither(json.get[Str](JsonCursor.field("mail").isString).map(_.value))
.mapError(InvalidRequest("Missing or invalid field mail", _))
password <-
ZIO.fromEither(json.get[Str](JsonCursor.field("password").isString).map(_.value.hashCode))
.mapError(InvalidRequest("Missing or invalid field password", _))
result <- run(quote { // TODO use argon2id
query[User]
.filter(usr => usr.mailAddress == lift(mail))
.filter(usr => usr.passwordHash == lift(password))
}).mapError(InternalError.apply)
yield result.headOption
}.someOrFail(InvalidPassword)
def post(request: Request) =
val bindSession =
for
body <- request
.body
.asString
.tapError(Console.printError(_))
.mapError(s =>
InvalidRequest("Wrong request body", s.getMessage)
)
json <- ZIO.fromEither(body.fromJson[Json])
.mapError(InvalidRequest("Invalid JSON body", _))
user <- getUser(json)
jwt <- ZIO.serviceWithZIO[Authentificator](_.requestJwt(user))
yield (user, jwt)
bindSession.map { case (_, jwt) =>
Response(
status = Status.Found,
headers = Headers.location("/") ++ //login successful, go back to main page
Headers.setCookie(Cookie("JWT", jwt)) //and set the token cookie
)
} fold( {
_ match
case TokenNotFound(_) => Response(
status = Status.Unauthorized,
body = errorBody("unauthorized", "unknown token"),
headers =
Headers(
Headers.location("/login")
) // send back caller to login panel
)
case UserNotFound(_) => Response(
status = Status.Unauthorized,
body = errorBody("unauthorized", "unknown user email"),
headers =
Headers(
Headers.location("/register")
) // send back caller to register panel
)
case InvalidPassword => Response(
status = Status.Unauthorized,
body = errorBody("unauthorized", "invalid password")
)
case InvalidRequest(msg, cause) => Response(
status = Status.Unauthorized,
body = errorBody("wrong request", s"$cause: $msg")
)
case InternalError(_) => Response(
status = Status.InternalServerError,
body = errorBody("internal", "internal error, please contact support")
)
}, x => x)

@ -0,0 +1,97 @@
package org.tbasket.handler
import io.getquill.*
import io.getquill.context.ZioJdbc.*
import io.getquill.context.qzio.{ZioContext, ZioJdbcContext}
import io.getquill.context.sql.idiom.SqlIdiom
import org.apache.logging.log4j.LogManager
import org.tbasket.auth.Authentificator
import org.tbasket.data.{DatabaseContext, User}
import org.tbasket.error.AuthException.*
import org.tbasket.error.ExceptionEnum
import org.tbasket.error.JwtException.*
import org.tbasket.error.RegularException.*
import org.tbasket.handler.HandlerUtils.errorBody
import zio.http.*
import zio.http.model.{Cookie, Header, Headers, Status}
import zio.json.*
import zio.json.ast.Json.Str
import zio.json.ast.{Json, JsonCursor}
import zio.{ZEnvironment, ZIO, *}
import java.sql.SQLException
import java.util.UUID
object LoginPageHandler extends PageHandler:
private val LOG = LogManager.getLogger("Login")
private def getUser(json: Json) =
ZIO.serviceWithZIO[Authentificator] { auth =>
for
mail <- HandlerUtils.parseAttribute(json, "mail", JsonCursor.field("name").isString)
password <- HandlerUtils.parseAttribute(json, "mail", JsonCursor.field("name").isString)
user <- auth.loginUser(mail, password)
yield user
}
def post(request: Request) =
val bindSession =
for
body <- request
.body
.asString
.mapError(s =>
InvalidRequest("Unparseable request body", s.getMessage)
)
json <- ZIO.fromEither(body.fromJson[Json])
.mapError(InvalidRequest("Invalid JSON body", _))
user <- getUser(json)
jwt <- ZIO.serviceWithZIO[Authentificator](_.requestNewJwt(user))
yield jwt
bindSession.map { jwt =>
Response(
status = Status.Found,
headers = Headers.location("/") ++ //login successful, go back to main page
Headers.setCookie(Cookie("JWT", jwt)) //and set the token cookie
)
}.catchAll {
case UserNotFound => ZIO.attempt(Response(
status = Status.Unauthorized,
body = errorBody("unauthorized", "unknown user email"),
headers =
Headers(
Headers.location("/register")
) // send back caller to register panel
))
case InvalidPassword => ZIO.attempt(Response(
status = Status.Unauthorized,
body = errorBody("unauthorized", "invalid password")
))
case InvalidRequest(msg, cause) => ZIO.attempt(Response(
status = Status.Unauthorized,
body = errorBody("invalid request", s"$cause: $msg")
))
case InternalError(e) =>
LOG.error("Internal error : ")
LOG.throwing(e)
ZIO.attempt(Response(
status = Status.InternalServerError,
body = errorBody("internal", "internal error, please contact support")
))
case other =>
LOG.error("Unhandle exception : ")
LOG.throwing(other)
ZIO.attempt(Response(
status = Status.InternalServerError,
body = errorBody("internal", "internal error, please contact support")
))
}

@ -0,0 +1,8 @@
package org.tbasket.handler
/**
* tag interface for page handlers
* */
trait PageHandler {
}

@ -0,0 +1,40 @@
package org.tbasket.handler
import org.tbasket.auth.Authentificator
import org.tbasket.data.User
import org.tbasket.error.RegularException.InvalidRequest
import org.tbasket.handler.HandlerUtils.parseAttribute
import zio.ZIO
import zio.http.model.{Cookie, Headers, Status}
import zio.http.{Request, Response, model}
import zio.json.ast.{Json, JsonCursor}
import zio.json.*
object RegisterPageHandler extends PageHandler {
private def tryPost(request: Request) =
for
body <- request.body.asString
.mapError(e => InvalidRequest("Invalid request body", e.getMessage))
json <- ZIO.fromEither(body.fromJson[Json])
.mapError(InvalidRequest("Invalid JSON body", _))
name <- parseAttribute(json, "name", JsonCursor.field("name").isString)
forename <- parseAttribute(json, "forename", JsonCursor.field("forename").isString)
mail <- parseAttribute(json, "mail", JsonCursor.field("mail").isString)
password <- parseAttribute(json, "password", JsonCursor.field("password").isString)
user <- ZIO.serviceWithZIO[Authentificator](_.registerUser(name, forename, mail, password))
jwt <- ZIO.serviceWithZIO[Authentificator](_.requestNewJwt(user))
yield Response(
status = Status.Found,
headers = Headers.location("/") ++ //register successful, go back to main page
Headers.setCookie(Cookie("JWT", jwt))
)
def post(request: Request) = tryPost(request).catchSome {
case x => ???
}
}

@ -25,7 +25,6 @@ import javax.crypto.SecretKey
class JwtGenerator(tokenLifespan: Duration, key: PrivateKey, algorithm: JwtAsymmetricAlgorithm): class JwtGenerator(tokenLifespan: Duration, key: PrivateKey, algorithm: JwtAsymmetricAlgorithm):
private def claims(content: String) = JwtClaim( private def claims(content: String) = JwtClaim(
expiration = Some(currentTimeMillis() + tokenLifespan.toMillis), expiration = Some(currentTimeMillis() + tokenLifespan.toMillis),
issuedAt = Some(currentTimeMillis()), issuedAt = Some(currentTimeMillis()),
@ -33,7 +32,6 @@ class JwtGenerator(tokenLifespan: Duration, key: PrivateKey, algorithm: JwtAsymm
content = content content = content
) )
def generateTokenResponse(request: Request): Task[Response] = def generateTokenResponse(request: Request): Task[Response] =
for for
claims <- request.body.asString.map(claims) claims <- request.body.asString.map(claims)

@ -46,4 +46,6 @@ object Core extends HttpModule { //also handles http
ivy"org.xerial:sqlite-jdbc:3.40.0.0", ivy"org.xerial:sqlite-jdbc:3.40.0.0",
) )
//override def scalacOptions = T { Seq("-explain") }
} }