All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.softwaremill.session.SessionEncoder.scala Maven / Gradle / Ivy

There is a newer version: 0.5.11
Show newest version
package com.softwaremill.session

import scala.util.Try

trait SessionEncoder[T] {
  def encode(t: T, nowMillis: Long, config: SessionConfig): String
  def decode(s: String, config: SessionConfig): Try[DecodeResult[T]]
}

object SessionEncoder {
  /**
   * Default low-priority implicit encoder. If you wish to use another one, provide an implicit encoder in a
   * higher-priority implicit scope, e.g. as an implicit value declared next to `SessionManager`.
   */
  implicit def basic[T](implicit serializer: SessionSerializer[T, String]) = new BasicSessionEncoder[T]()
}

case class DecodeResult[T](t: T, expires: Option[Long], signatureMatches: Boolean)

/**
 * @param serializer Must create cookie-safe strings (only with allowed characters).
 */
class BasicSessionEncoder[T](implicit serializer: SessionSerializer[T, String]) extends SessionEncoder[T] {

  override def encode(t: T, nowMillis: Long, config: SessionConfig) = {
    // adding an "x" so that the string is never empty, even if there's no data
    val serialized = "x" + serializer.serialize(t)

    val withExpiry = config.sessionMaxAgeSeconds.fold(serialized) { maxAge =>
      val expiry = nowMillis + maxAge * 1000L
      s"$expiry-$serialized"
    }

    val encrypted = if (config.sessionEncryptData) Crypto.encrypt_AES(withExpiry, config.serverSecret) else withExpiry

    s"${Crypto.sign_HmacSHA1_hex(serialized, config.serverSecret)}-$encrypted"
  }

  override def decode(s: String, config: SessionConfig) = {
    def extractExpiry(data: String): (Option[Long], String) = {
      config.sessionMaxAgeSeconds.fold((Option.empty[Long], data)) { maxAge =>
        val splitted = data.split("-", 2)
        (Some(splitted(0).toLong), splitted(1))
      }
    }

    Try {
      val splitted = s.split("-", 2)
      val decrypted = if (config.sessionEncryptData) Crypto.decrypt_AES(splitted(1), config.serverSecret) else splitted(1)

      val (expiry, serialized) = extractExpiry(decrypted)

      val signatureMatches = SessionUtil.constantTimeEquals(
        splitted(0),
        Crypto.sign_HmacSHA1_hex(serialized, config.serverSecret)
      )

      serializer.deserialize(serialized.substring(1)).map {
        DecodeResult(_, expiry, signatureMatches)
      }
    }.flatten
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy