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

ai.starlake.utils.TransformEngine.scala Maven / Gradle / Ivy

package ai.starlake.utils

import java.security.SecureRandom

/** Several encryption methods used in privacy management
  */
object TransformEngine {

  def algo(alg: String, data: String): String = {
    val m = java.security.MessageDigest.getInstance(alg)
    val b = data.getBytes("UTF-8")
    m.update(b, 0, b.length)
    val bytes: Array[Byte] = m.digest()
    bytes.map("%02x" format _).mkString
  }

  def parse(maskingAlgo: String): (String, List[String]) = {
    def parseParams(params: List[String]): List[String] =
      params.map { param =>
        if (param.startsWith("\"") && param.endsWith("\""))
          param.substring(1, param.length - 1)
        else if (param.startsWith("'") && param.endsWith("'"))
          param.substring(1, 2)
        else
          param
      }

    val hasParam = maskingAlgo.indexOf('(')
    if (hasParam > 0) {
      assert(maskingAlgo.indexOf(')') > hasParam)
      (
        maskingAlgo.substring(0, hasParam),
        parseParams(
          maskingAlgo.substring(hasParam + 1, maskingAlgo.length - 1).split(',').map(_.trim).toList
        )
      )
    } else {
      (maskingAlgo, Nil)
    }
  }
}

trait TransformEngine {

  /** @param s:
    *   String => Input string to encrypt
    * @param colMap
    *   : Map[String, Option[String]] => Map of all the attributes and their corresponding values
    * @param params:
    *   List[Any] => Parameters passed to the algorithm as defined in the conf file. Parameter
    *   starting with '"' is converted to a string Parameter containing a '.' is converted to a
    *   double Parameter equals to true of false is converted a boolean Anything else is converted
    *   to an int
    * @return
    *   The encrypted string
    */
  def crypt(s: String, colMap: => Map[String, Option[String]], params: List[String]): String
}

object Md5 extends TransformEngine {

  def crypt(s: String, colMap: => Map[String, Option[String]], params: List[String]): String =
    TransformEngine.algo("MD5", s)
}

object Sha1 extends TransformEngine {

  def crypt(s: String, colMap: => Map[String, Option[String]], params: List[String]): String =
    TransformEngine.algo("SHA-1", s)
}

object Sha256 extends TransformEngine {

  def crypt(s: String, colMap: => Map[String, Option[String]], params: List[String]): String =
    TransformEngine.algo("SHA-256", s)
}

object Sha512 extends TransformEngine {

  def crypt(s: String, colMap: => Map[String, Option[String]], params: List[String]): String =
    TransformEngine.algo("SHA-512", s)
}

object Hide extends TransformEngine {

  def crypt(s: String, colMap: => Map[String, Option[String]], params: List[String]): String = {
    if (params.isEmpty)
      ""
    else {
      assert(params.length == 2)
      val c = params.head
      val i = params(1).toInt
      c * i
    }
  }
}

object No extends TransformEngine {
  def crypt(s: String, colMap: => Map[String, Option[String]], params: List[String]): String = s
}

object Initials extends TransformEngine {

  def crypt(s: String, colMap: => Map[String, Option[String]], params: List[String]): String = {
    s.split("\\s+").map(_.substring(0, 1)).mkString("", ".", ".")
  }
}

object Email extends TransformEngine {

  def crypt(s: String, colMap: => Map[String, Option[String]], params: List[String]): String = {
    assert(params.length == 1)
    val split = s.split('@')
    TransformEngine.algo(params.head.toString, split(0)) + "@" + split(1)
  }
}

trait IP extends TransformEngine {
  def separator: Char

  def crypt(s: String, colMap: => Map[String, Option[String]], params: List[String]): String = {
    assert(params.length == 1)
    crypt(s, params.head.toInt)
  }

  def crypt(s: String, maskBytes: Int): String = {
    val ip = s.split(separator)
    (ip.dropRight(maskBytes) ++ List.fill(maskBytes)(0)).mkString(separator.toString)
  }
}

object IPv4 extends IP {
  override val separator: Char = '.'
}

object IPv6 extends IP {
  override val separator: Char = ':'
}

trait NumericRandomTransform extends TransformEngine {
  val rnd: SecureRandom

  final def gen(low: Double, up: Double): Double = low + (up - low) * rnd.nextDouble()

  def genUnbounded(): Double

  final def crypt(params: List[String]): Double = {
    assert(params.length == 2 || params.isEmpty)
    params match {
      case Nil =>
        genUnbounded()
      case lowerBound :: upperBound :: Nil =>
        val low = lowerBound.toDouble
        val up = upperBound.toDouble
        gen(low, up)
      case _ => throw new Exception("Should never happen!")
    }
  }
}

object RandomDouble extends NumericRandomTransform {
  val rnd = new SecureRandom()

  def genUnbounded(): Double = rnd.nextDouble()

  override def crypt(
    s: String,
    colMap: => Map[String, Option[String]],
    params: List[String]
  ): String = {
    crypt(params).toString
  }
}

object RandomLong extends NumericRandomTransform {
  val rnd = new SecureRandom()

  override def genUnbounded(): Double = rnd.nextLong().toDouble

  override def crypt(
    s: String,
    colMap: => Map[String, Option[String]],
    params: List[String]
  ): String =
    (crypt(params) % Long.MaxValue).toLong.toString
}

object RandomInt extends NumericRandomTransform {
  val rnd = new SecureRandom()

  override def genUnbounded(): Double = rnd.nextInt().toDouble

  override def crypt(
    s: String,
    colMap: => Map[String, Option[String]],
    params: List[String]
  ): String =
    (crypt(params) % Int.MaxValue).toInt.toString
}

class ApproxDouble extends TransformEngine {
  val rnd = new SecureRandom()

  def crypt(s: String, colMap: => Map[String, Option[String]], params: List[String]): String = {
    assert(params.length == 1)
    crypt(s.toDouble, params.head.toInt).toString
  }

  def crypt(value: Double, percent: Int): Double = {
    val rndBool = rnd.nextBoolean()
    val distance = (value * percent * rnd.nextDouble()) / 100
    if (rndBool)
      value - distance
    else
      value + distance
  }
}

object ApproxDouble extends ApproxDouble

object ApproxLong extends ApproxDouble {

  override def crypt(
    s: String,
    colMap: => Map[String, Option[String]],
    params: List[String]
  ): String = {
    assert(params.length == 1)
    crypt(s.toDouble, params.head.toInt).toLong.toString
  }

}

object Mask extends TransformEngine {

  def crypt(s: String, colMap: => Map[String, Option[String]], params: List[String]): String = {
    assert(params.length == 4)
    val maskingChar = params(0).charAt(0)
    val numberOfChars = params(1).toInt
    val leftSide = params(2).toInt
    val rightSide = params(3).toInt
    crypt(s, maskingChar, numberOfChars, leftSide, rightSide)
  }

  def crypt(
    s: String,
    maskingChar: Char,
    numberOfChars: Int,
    leftSide: Int,
    rightSide: Int
  ): String = {
    s match {
      case input if input.length <= leftSide =>
        "%s%s".format(input, maskingChar.toString * numberOfChars)
      case _ =>
        "%s%s%s".format(
          s.take(leftSide),
          maskingChar.toString * numberOfChars,
          s.takeRight(rightSide)
        )
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy