From ac49d60f3140637b97c7f4618bbefbdf62da0710 Mon Sep 17 00:00:00 2001 From: Override-6 Date: Tue, 24 Jan 2023 22:19:17 +0100 Subject: [PATCH] workin on JWT Emmiter and trying to understand how zio handles exceptions --- .../src/org/tbasket/jwt/JwtGeneration.scala | 12 ---- .../src/org/tbasket/jwt/JwtGenerator.scala | 35 ++++++++++ JWTEmitter/src/org/tbasket/jwt/Main.scala | 64 ++++++++++--------- build.sc | 4 +- 4 files changed, 73 insertions(+), 42 deletions(-) delete mode 100644 JWTEmitter/src/org/tbasket/jwt/JwtGeneration.scala create mode 100644 JWTEmitter/src/org/tbasket/jwt/JwtGenerator.scala diff --git a/JWTEmitter/src/org/tbasket/jwt/JwtGeneration.scala b/JWTEmitter/src/org/tbasket/jwt/JwtGeneration.scala deleted file mode 100644 index 9d178bc..0000000 --- a/JWTEmitter/src/org/tbasket/jwt/JwtGeneration.scala +++ /dev/null @@ -1,12 +0,0 @@ -package org.tbasket.jwt - -import zio.Task -import zio.http.{Request, Response} - -object JwtGeneration { - - def generateTokenResponse(request: Request): Task[Response] = { - ??? - } - -} diff --git a/JWTEmitter/src/org/tbasket/jwt/JwtGenerator.scala b/JWTEmitter/src/org/tbasket/jwt/JwtGenerator.scala new file mode 100644 index 0000000..f7ad17a --- /dev/null +++ b/JWTEmitter/src/org/tbasket/jwt/JwtGenerator.scala @@ -0,0 +1,35 @@ +package org.tbasket.jwt + +import pdi.jwt.* +import zio.* +import zio.http.{Request, Response} +import zio.json.* +import zio.json.ast.Json + +import java.lang.System.currentTimeMillis +import java.nio.file.* +import java.security.Key +import java.security.interfaces.RSAPrivateKey +import java.security.spec.PKCS8EncodedKeySpec +import java.util.concurrent.TimeUnit +import java.util.{Date, UUID} +import javax.crypto.SecretKey +import scala.concurrent.duration + +object JwtGenerator: + + private val ExpirationDate = Duration(15, TimeUnit.DAYS).toMillis + private val Key = Files.readString(Path.of("id_rsa")) + + private def claims(content: String) = JwtClaim( + expiration = Some(currentTimeMillis() + ExpirationDate), + issuedAt = Some(currentTimeMillis()), + jwtId = Some(UUID.randomUUID().toString), + content = content + ) + + def generateTokenResponse(request: Request): Task[Response] = + for + claims <- request.body.asString.map(claims) + jwt <- ZIO.attempt(JwtZIOJson.encode(claims, Key, JwtAlgorithm.RS256)) + yield Response.json(jwt) diff --git a/JWTEmitter/src/org/tbasket/jwt/Main.scala b/JWTEmitter/src/org/tbasket/jwt/Main.scala index f19b479..67d98ee 100644 --- a/JWTEmitter/src/org/tbasket/jwt/Main.scala +++ b/JWTEmitter/src/org/tbasket/jwt/Main.scala @@ -4,34 +4,40 @@ import zio.* import zio.stream.* import zio.http.* import zio.http.ServerConfig.LeakDetectionLevel -import zio.http.model.Method - -object Main extends ZIOAppDefault { - - private val app = Http.collectZIO[Request] { - case r@Method.POST -> !! / "/jwt" => - JwtGeneration.generateTokenResponse(r) - +import zio.http.model.{Method, Status} + +import java.nio.file.Files + +object Main extends ZIOAppDefault: + + private def port(args: Chunk[String]): Task[Int] = + args.headOption match + case None => ZIO.dieMessage("Must provide the port argument") + case Some(head) => head.toIntOption match + case Some(port) if port > 0 && port < 65536 => ZIO.succeed(port) + case Some(oorPort) => ZIO.dieMessage(s"'$oorPort' is out of range.'") + case None => ZIO.dieMessage("given argument is not a valid integer") + + private val app = Http.collectZIO[Request][Any, Throwable, Response] { + case r @ Method.GET -> _ / "jwt" => + ZIO.attempt(JwtGenerator) + .flatMap(_.generateTokenResponse(r)) + .catchAll(e => ZIO.die(e)) + case _ => + ZIO.succeed(Response(status = Status.NotFound)) } - + + private def startServer(port: Int) = + val config = ServerConfig.default + .port(port) + .leakDetection(LeakDetectionLevel.PARANOID) + + val configLayer = ServerConfig.live(config) + (Server.install( + app + ) *> Console.printLine(s"JWT AppToken open on port $port") *> ZIO.never) + .provide(configLayer, Server.live) + val run = - ZIO.serviceWithZIO[ZIOAppArgs] { args => - for - port <- ZIO.attempt(args.getArgs) - .map(_.head) - .mapError(_ => Console.printError("Must provide at least one argument")) - .map(_.toInt) - .mapError(_ => Console.printError("Port is not a number")) - .filterOrFail(p => 0 < p && p < 65563)(Console.printError("Port is out of range")) - - yield - val config = ServerConfig.default - .port(port) - .leakDetection(LeakDetectionLevel.PARANOID) - - val configLayer = ServerConfig.live(config) - (Server.install(app) *> Console.printLine("JWT AppToken open on port") *> ZIO.never) - .provideSome(configLayer, Server.live) - } - -} + ZIO.serviceWithZIO[ZIOAppArgs](args => port(args.getArgs)) + .flatMap(startServer) diff --git a/build.sc b/build.sc index ee7604c..3b55b1d 100644 --- a/build.sc +++ b/build.sc @@ -5,6 +5,7 @@ trait ServerModule extends ScalaModule with ScalafmtModule { //override def scalacOptions = Seq("-explain") override final def scalaVersion = "3.2.0" + override def ivyDeps = Agg( ivy"dev.zio::zio:2.0.6", ivy"org.apache.logging.log4j:log4j-slf4j-impl:2.19.0" @@ -14,7 +15,8 @@ trait HttpModule extends ServerModule { override def ivyDeps = super.ivyDeps() ++ Agg( ivy"dev.zio::zio-http:0.0.3", ivy"dev.zio::zio-json:0.4.2", - ivy"dev.zio::zio-streams:2.0.6" + ivy"dev.zio::zio-streams:2.0.6", + ivy"com.github.jwt-scala::jwt-zio-json:9.1.2" ) }