Last active
May 2, 2019 10:34
-
-
Save stsatlantis/554de0d9c5eff3dad5001568f86a4715 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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") | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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