Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Try to fix dropping Play 2.7... #450

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 1 addition & 43 deletions play/play-v28/RotatingSecretComponents.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@ import play.api.http._
import play.api.mvc._
import play.api.mvc.request.{DefaultRequestFactory, RequestFactory}
import play.api.{BuiltInComponentsFromContext, Configuration}
import play.api.libs.crypto.{CSRFTokenSigner, CSRFTokenSignerProvider, DefaultCSRFTokenSigner, DefaultCookieSigner}
import java.security.MessageDigest
import com.github.blemale.scaffeine.{Scaffeine, LoadingCache}
import scala.concurrent.duration._


trait RotatingSecretComponents extends BuiltInComponentsFromContext {
Expand All @@ -20,14 +16,11 @@ trait RotatingSecretComponents extends BuiltInComponentsFromContext {
override def configuration: Configuration = {
val nonRotatingSecretOnlyUsedToSatisfyConfigChecks = secretStateSupplier.snapshot().secrets.active

super.configuration.withFallback(Configuration("play.http.secret.key" -> nonRotatingSecretOnlyUsedToSatisfyConfigChecks))
super.configuration ++ Configuration("play.http.secret.key" -> nonRotatingSecretOnlyUsedToSatisfyConfigChecks)
}

override lazy val requestFactory: RequestFactory =
RotatingSecretComponents.requestFactoryFor(secretStateSupplier, httpConfiguration)

override lazy val csrfTokenSigner: CSRFTokenSigner =
new RotatingSecretComponents.RotatingKeyCSRFTokenSigner(secretStateSupplier, systemUTC())
}

object RotatingSecretComponents {
Expand Down Expand Up @@ -66,39 +59,4 @@ object RotatingSecretComponents {
val snapshotProvider: SnapshotProvider) extends FlashCookieBaker with RotatingSecretCookieCodec {
override val jwtConfiguration: JWTConfiguration = config.jwt
}

class RotatingKeyCSRFTokenSigner(
snapshotProvider: SnapshotProvider,
clock: Clock) extends CSRFTokenSigner {

private val signerCache: LoadingCache[String, DefaultCSRFTokenSigner] = Scaffeine()
.expireAfterAccess(1.day) // stop the cache growing indefinitely
.build(secret => new DefaultCSRFTokenSigner(new DefaultCookieSigner(SecretConfiguration(secret)), clock))

private def signerForActiveSecret() = signerCache.get(snapshotProvider.snapshot().secrets.active)

override def signToken(token: String): String = signerForActiveSecret().signToken(token)
override def generateToken: String = signerForActiveSecret().generateToken
override def generateSignedToken: String = signerForActiveSecret().generateSignedToken
override def constantTimeEquals(a: String, b: String): Boolean = signerForActiveSecret().constantTimeEquals(a, b)

/**
* This method verifies tokens which may have been signed with a previous secret that we still consider valid
* for now. It tries all applicable secrets to see if any of them can verify the token.
*/
override def extractSignedToken(token: String): Option[String] =
snapshotProvider.snapshot().decodeOpt(secret => signerCache.get(secret).extractSignedToken(token))

/**
* It's important that this method doesn't just delegate to an underlying `DefaultCSRFTokenSigner`, because this
* method uses the `extractSignedToken()` method, and we need to use the tolerant version of that method that's
* only available in _this_ class.
*/
override def compareSignedTokens(tokenA: String, tokenB: String): Boolean = {
for {
rawA <- extractSignedToken(tokenA)
rawB <- extractSignedToken(tokenB)
} yield MessageDigest.isEqual(rawA.getBytes("utf-8"), rawB.getBytes("utf-8"))
}.getOrElse(false)
}
}