Skip to content

Instantly share code, notes, and snippets.

@stsatlantis
Last active May 2, 2019 10:34
Show Gist options
  • Save stsatlantis/554de0d9c5eff3dad5001568f86a4715 to your computer and use it in GitHub Desktop.
Save stsatlantis/554de0d9c5eff3dad5001568f86a4715 to your computer and use it in GitHub Desktop.
import io.circe.Encoder
import io.circe.refined.refinedEncoder
import shapeless._
import shapeless.ops.coproduct.{ Mapper => CoproductMapper }
import shapeless.ops.hlist.{ Mapper => HListMapper }
import shapeless.tag.@@
trait Sensitive
object Sensitive {
type SensitiveString = String @@ Sensitive
def apply[T](input: T): T @@ Sensitive = tag[Sensitive][T](input)
implicit val sensitiveEncoder: Encoder[SensitiveString] = refinedEncoder
}
object Mask {
import Sensitive._
private trait LowPrioMaskPoly extends Poly1 {
implicit def defaultMapper[T]: Case.Aux[T, T] = {
at[T](identity[T])
}
}
private object MaskPoly extends LowPrioMaskPoly {
implicit val sensitiveMasker: Case.Aux[SensitiveString, SensitiveString] = {
at[SensitiveString] { _ => tag[Sensitive][String]("***") }
}
implicit val optionMasker: Case.Aux[Option[SensitiveString], Option[SensitiveString]] = {
at[Option[SensitiveString]] {
_.map(_ => tag[Sensitive][String]("***"))
}
}
implicit def genericMapper[T, L <: HList](implicit
gen: Generic.Aux[T, L],
hListMapper: HListMapper.Aux[MaskPoly.type, L, L]): Case.Aux[T, T] = {
at[T] { input =>
gen.from(gen.to(input).map(MaskPoly))
}
}
}
def hideSensitiveData[T, L <: HList](input: T)(
implicit
gen: Generic.Aux[T, L],
hListMapper: HListMapper.Aux[MaskPoly.type, L, L]
): T = {
gen.from(gen.to(input).map(MaskPoly))
}
}
import Sensitive.SensitiveString
import org.scalatest.{Matchers, WordSpec}
import shapeless.tag
case class Person(email: SensitiveString, age: Int, nickname: String)
class MaskSpec extends WordSpec with Matchers {
"Person with sensitive data" in {
val peter = Person(Sensitive[String]("peter@localhost"), 2, "Peter")
Mask.hideSensitiveData(peter) shouldBe Person(Sensitive[String]("***"), 2, "Peter")
}
}
package encryption
import encryption.Mapper.{Encryptable, EncryptableString}
import shapeless._
import tag.@@
trait Mapper[T] {
def apply(input: T, encryption: EncryptableString => EncryptableString): T
}
trait LowPrioMapper {
implicit def defaultEncpytion[T]: Mapper[T] = new Mapper[T] {
override def apply(input: T, f: EncryptableString => EncryptableString): T = input
}
}
object EncryptableString{
def lift(f : String => String) : EncryptableString => EncryptableString = e => tag[Encryptable][String](f(e))
}
object Mapper extends LowPrioMapper {
def apply[T](implicit Mapper: Mapper[T]): Mapper[T] = Mapper
trait Encryptable
type EncryptableString = String @@ Encryptable
implicit def encryptalbeEncryption(implicit ev:EncryptableString <:< String): Mapper[EncryptableString] = new Mapper[EncryptableString] {
override def apply(input: EncryptableString, f: EncryptableString => EncryptableString): EncryptableString = {
f(input)
}
}
implicit def hListEncryption[H, T <: HList](
implicit
headEncryption: Lazy[Mapper[H]],
tailEncryption: Mapper[T]
): Mapper[H :: T] = new Mapper[H :: T] {
override def apply(input: H :: T, encryption: EncryptableString => EncryptableString): H :: T = {
headEncryption.value.apply(input.head, encryption) :: tailEncryption.apply(input.tail, encryption)
}
}
implicit def genericEncryption[T, L <: HList](
implicit
gen: Generic.Aux[T, L],
hListEncryption: Mapper[L]
): Mapper[T] = new Mapper[T] {
override def apply(input: T, encryption: EncryptableString => EncryptableString): T = {
val encypt: L => L = input => hListEncryption(input, encryption)
(gen.to _).andThen(encypt).andThen(gen.from)(input)
}
}
}
case class Goat(name: EncryptableString, age: Int)
val goat = Goat(tag[Encryptable][String]("Kecske") , 1)
val asd : EncryptableString => EncryptableString = EncryptableString.lift(_.toUpperCase)
Mapper[Goat].apply(goat, asd) shouldBe Goat(tag[Encryptable][String]("KECSKE"), 1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment