diff --git a/.gitignore b/.gitignore index da91fd6..cba59ec 100644 --- a/.gitignore +++ b/.gitignore @@ -3,8 +3,9 @@ build out */.gradle/ .bsp -log +keys +log *.sqlite *.$* diff --git a/JWTEmitter/src/org/tbasket/jwt/JwtGenerator.scala b/JWTEmitter/src/org/tbasket/jwt/JwtGenerator.scala index 88af336..34c0de7 100644 --- a/JWTEmitter/src/org/tbasket/jwt/JwtGenerator.scala +++ b/JWTEmitter/src/org/tbasket/jwt/JwtGenerator.scala @@ -9,6 +9,7 @@ import zio.json.ast.Json import java.lang.System.currentTimeMillis import java.nio.file.* +import java.security.cert.CertificateFactory import java.security.{Key, PrivateKey} import java.security.interfaces.RSAPrivateKey import java.security.spec.PKCS8EncodedKeySpec @@ -33,4 +34,5 @@ class JwtGenerator(tokenLifespan: Duration, key: PrivateKey, algorithm: JwtAsymm jwt <- ZIO.attempt(JwtZIOJson.encode(claims, key, algorithm)).catchAll(e => { ZIO.attempt(e.printStackTrace()).as("error") }) - yield Response.json(jwt) + yield + Response.json(jwt) diff --git a/JWTEmitter/src/org/tbasket/jwt/Main.scala b/JWTEmitter/src/org/tbasket/jwt/Main.scala index 48a2739..84ed5fb 100644 --- a/JWTEmitter/src/org/tbasket/jwt/Main.scala +++ b/JWTEmitter/src/org/tbasket/jwt/Main.scala @@ -10,20 +10,47 @@ import zio.stream.* import java.nio.file.{Files, Path} import java.security.{KeyFactory, PrivateKey} -import java.security.spec.{KeySpec, PKCS8EncodedKeySpec, RSAPrivateKeySpec, X509EncodedKeySpec} +import java.security.spec.{ + KeySpec, + PKCS8EncodedKeySpec, + RSAPrivateKeySpec, + X509EncodedKeySpec +} import java.time.Duration import scala.util.chaining.scalaUtilChainingOps object Main extends ZIOAppDefault: - private def port(args: Chunk[String]): Task[Int] = - args.headOption match + private val KeyFactory = java.security.KeyFactory.getInstance("RSA") + + private def parsePort(port: Option[String]): Task[Int] = + port match case None => ZIO.dieMessage("Must provide the port argument") - case Some(head) => head.toIntOption match + case Some(port) => port.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 def loadKey(keyFile: Option[String]): Task[PrivateKey] = + keyFile match + case None => ZIO.dieMessage("Key RSA File not given") + case Some(file) => + ZIO + .attempt(Path.of(file)) + .mapAttempt(Files.readAllBytes) + .mapAttempt(new PKCS8EncodedKeySpec(_)) + .mapAttempt(KeyFactory.generatePrivate) + + private def parseArgs(args: Chunk[String]): Task[(Int, PrivateKey)] = + def groups = args.toList.grouped(2) + val keyFile = groups.collectFirst { + case ("-k" | "--key") :: value :: Nil => value + } + val port = groups.collectFirst { + case ("-p" | "--port") :: value :: Nil => value + } + parsePort(port) <&> loadKey(keyFile) + private val app = Http.collectZIO[Request] { case r @ Method.GET -> _ / "jwt" => ZIO.serviceWithZIO[JwtGenerator](_.generateTokenResponse(r)) @@ -31,15 +58,13 @@ object Main extends ZIOAppDefault: ZIO.succeed(Response(status = Status.NotFound)) } - private def startServer(port: Int) = + private def startServer(port: Int, key: PrivateKey) = val config = ServerConfig.default .port(port) .leakDetection(LeakDetectionLevel.PARANOID) - val pbytes = Files.readAllBytes(Path.of("/home/maxime/tmp/key.pcqks")) - val key: PrivateKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(pbytes)) - - val generator = new JwtGenerator(Duration.ofDays(15), key, JwtAlgorithm.RS256) + val generator = + new JwtGenerator(Duration.ofDays(15), key, JwtAlgorithm.RS256) val configLayer = ServerConfig.live(config) (Server.install( app @@ -47,5 +72,5 @@ object Main extends ZIOAppDefault: .provide(configLayer, Server.live, ZLayer.succeed(generator)) val run = - ZIO.serviceWithZIO[ZIOAppArgs](args => port(args.getArgs)) + ZIO.serviceWithZIO[ZIOAppArgs](args => parseArgs(args.getArgs)) .flatMap(startServer)