Saturday, October 20, 2018

Setting up work with MongoDB in Scala

MongoDB provides a reactive driver what allows to forget about thread pool when working with the database. Another reason to choose MongoDB it's to store or load case classes without writing a mapping between case class and document.
So, at the begin add a driver to the dependencies:
"org.mongodb.scala" %% "mongo-scala-driver"   % "2.2.1",
"io.netty"           % "netty-transport"      % "4.1.29.Final",
"io.netty"           % "netty-handler"        % "4.1.29.Final"
Create mongo client and open database:
private val mongoClient = MongoClient(config.getString("db.url"))
private val omsDatabase: MongoDatabase = mongoClient.getDatabase("mydb")
Let's define a simple case class
case class User(_id: ObjectId, name: String, password: String)
To save it in the database as it is we need to generate mapping to document and register it to the codec registry
import org.mongodb.scala.bson.codecs.DEFAULT_CODEC_REGISTRY
import org.bson.codecs.configuration.CodecRegistries.{ fromProviders, fromRegistries }
import org.mongodb.scala.bson.codecs.Macros._

private val codecRegistry = fromRegistries(fromProviders(classOf[User]), DEFAULT_CODEC_REGISTRY)
Now let's open collection with this codec registry
private val collection: MongoCollection[User] = db.withCodecRegistry(codecRegistry).getCollection("collectionName")
That is all, now we can save and load our case class
collection.insertOne(User(new ObjectId, "username", "password"))
val users: Observable[User] = collection.find()
But to case class User has two issues if you want to send it through http as json. First of all spray does not know how to work with class ObjectId, second - property _id open information what a database is behind. So, to (de)serialize ObjectId we need to define an implicit function
  implicit object ObjectIdFormat extends JsonFormat[ObjectId] {
    def write(x: ObjectId) = JsString(x.toString)
    def read(value: JsValue): ObjectId = value match {
      case JsString(x) => new ObjectId(x)
      case x => deserializationError("Expected hex string, but got " + x)
    }
  }
Next, to serialize _id as id we need to fix a list of names what spray extracs from case classes. Spray does it in the class spray.json.ProductFormats.
trait CustomProductFormats extends ProductFormats {
  this: StandardFormats =>

  override protected def extractFieldNames(classManifest: ClassManifest[_]): Array[String] = {
    val fields = super.extractFieldNames(classManifest)
    fields.map(name => if (name == "_id") "id" else name)
  }
}
And last step is to create a copy of spray.json.DefaultJsonProtocol and to use our CustomProductFormats instead of ProductFormats
object JsonFormats extends BasicFormats
  with StandardFormats
  with CollectionFormats
  with CustomProductFormats
  with AdditionalFormats

Saturday, October 6, 2018

Simple way to secure Akka Http rest services

One of the differences between Play Framework and Akka Http it is a security of the rest services. Akka Http has almost nothing for that. So, let's assume the next behavior for our application:
  •  All rest services except login should be secured
  • Login service returns JWT token as part of the header
  • Before to access any other services we have to validate JWT token in the header of a request
Our route without security has next view:
lazy val routes: Route =
pathPrefix("api") {
    pathPrefix("login") {
      post {
        parameter("email", "password") { (email, password) =>
          val loginResult: Future[Boolean] = loginService.login(email, password)
          onSuccess(loginResult) { result =>
            if(result) {
              respondWithHeader(RawHeader("TOKEN", createJwtToken(email))) {
                complete(StatusCodes.OK)
              }else{
                complete(StatusCodes.Unauthorized)
              }
            }
          }
        }
      }
    } ~
    pathPrefix("myService") {
      val data = myService.search()
      complite(data)
    }      
}
Now let's write a method which will wrap invocation of all services which we want to secure:
def authenticated: Directive1[String] =
    Directives.optionalHeaderValueByName("TOKEN").flatMap {
      case Some(jwt) if !isJwtTokenValid(jwt) =>
        Directives.complete(StatusCodes.Unauthorized -> "Invalid token.")
      case Some(jwt) if isJwtTokenExpired(jwt) =>
        Directives.complete(StatusCodes.Unauthorized -> "Token expired.")

      case Some(jwt) if !isJwtTokenExpired(jwt) => decodeJwtToken(jwt) match {
        case Some(email) =>
          Directives.provide(email)
        case None =>
          Directives.complete(StatusCodes.Unauthorized)
      }
      case _ => Directives.complete(StatusCodes.Unauthorized)
    }
And the last step is to invoke it before our services:

pathPrefix("api") {
    pathPrefix("login") {
      ...
    } ~
    authenticated{email =>    
      pathPrefix("myService") {
        val data = myService.search()
        complite(data)
      }      
    }
}