Skip to content

Commit

Permalink
Add UseOnceSecret and ConfigValue#useOnceSecret
Browse files Browse the repository at this point in the history
  • Loading branch information
vlovgr committed Apr 23, 2021
1 parent f1128a2 commit eb08a87
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 0 deletions.
23 changes: 23 additions & 0 deletions modules/core/src/main/scala/ciris/ConfigValue.scala
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,29 @@ sealed abstract class ConfigValue[+F[_], A] {
override final def toString: String =
"ConfigValue$" + System.identityHashCode(this)
}

/**
* Returns a new [[ConfigValue]] which treats the value
* as a secret which can only be used once.
*
* Sensitive details are redacted from error messages. The
* value is wrapped in [[UseOnceSecret]] which prevents
* multiple accesses to the secret and which nullifies
* the secret once it has been used.
*
* Using `.useOnceSecret` is equivalent to using
* `.redacted.evalMap(UseOnceSecret(_))`.
*/
final def useOnceSecret(implicit ev: A <:< Array[Char]): ConfigValue[F, UseOnceSecret] =
new ConfigValue[F, UseOnceSecret] {
override final def to[G[x] >: F[x]](
implicit G: Async[G]
): Resource[G, ConfigEntry[UseOnceSecret]] =
self.redacted.to[G].evalMap(_.traverse(UseOnceSecret[G](_)))

override final def toString: String =
"ConfigValue$" + System.identityHashCode(this)
}
}

/**
Expand Down
41 changes: 41 additions & 0 deletions modules/core/src/main/scala/ciris/UseOnceSecret.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright 2017-2021 Viktor Lövgren
*
* SPDX-License-Identifier: MIT
*/

package ciris

import cats.effect.kernel.{Resource, Sync}
import cats.implicits._
import java.util.Arrays
import java.util.concurrent.atomic.AtomicReference

trait UseOnceSecret {
def resource[F[_]](implicit F: Sync[F]): Resource[F, Array[Char]]

final def useOnce[F[_], A](f: Array[Char] => F[A])(implicit F: Sync[F]): F[A] =
resource[F].use(f)
}

object UseOnceSecret {
final def apply[F[_]](secret: Array[Char])(implicit F: Sync[F]): F[UseOnceSecret] =
F.delay(new AtomicReference(secret.some)).map { ref =>
new UseOnceSecret {
override final def resource[G[_]](implicit G: Sync[G]): Resource[G, Array[Char]] = {
val acquire: G[Array[Char]] =
G.delay(ref.getAndSet(None)).flatMap {
case Some(secret) =>
G.pure(secret)
case None =>
G.raiseError(new IllegalStateException("Secret has already been used once"))
}

val release: G[Unit] =
G.delay(Arrays.fill(secret, ' '))

Resource.make(acquire)(_ => release)
}
}
}
}

0 comments on commit eb08a87

Please sign in to comment.