diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3cfea23..e62bdcb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,9 +14,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: corretto java-version: 11 diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 0000000..8079c02 --- /dev/null +++ b/.tool-versions @@ -0,0 +1 @@ +java corretto-11.0.22.7.1 diff --git a/build.sbt b/build.sbt index cf44733..c4d26fb 100644 --- a/build.sbt +++ b/build.sbt @@ -12,9 +12,6 @@ lazy val baseSettings = Seq( lazy val crossCompileScala3 = crossScalaVersions := Seq(scalaVersion.value, "3.3.3") -// Until all dependencies are on scala-java8-compat v1.x, this avoids unnecessary fatal eviction errors -ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-java8-compat" % VersionScheme.Always - lazy val core = project.settings(crossCompileScala3, baseSettings).settings( libraryDependencies ++= Seq( @@ -29,7 +26,7 @@ lazy val `aws-parameterstore-secret-supplier-base` = project.in(file("aws-parameterstore/secret-supplier")).settings(crossCompileScala3, baseSettings).dependsOn(core) val awsSdkForVersion = Map( - 1 -> "com.amazonaws" % "aws-java-sdk-ssm" % "1.12.692", + 1 -> "com.amazonaws" % "aws-java-sdk-ssm" % "1.12.693", 2 -> "software.amazon.awssdk" % "ssm" % "2.25.22" ) @@ -54,9 +51,8 @@ lazy val `aws-parameterstore-lambda` = project.in(file("aws-parameterstore/lambd lazy val `secret-generator` = project.settings(crossCompileScala3, baseSettings) val exactPlayVersions = Map( - "27" -> "com.typesafe.play" %% "play" % "2.7.9", - "28" -> "com.typesafe.play" %% "play" % "2.8.20", - "29" -> "com.typesafe.play" %% "play" % "2.9.0", + "28" -> "com.typesafe.play" %% "play" % "2.8.21", + "29" -> "com.typesafe.play" %% "play" % "2.9.2", "30" -> "org.playframework" %% "play" % "3.0.2" ) @@ -68,7 +64,6 @@ def playVersion(majorMinorVersion: String)= { } -lazy val `play-v27` = playVersion("27") lazy val `play-v28` = playVersion("28") lazy val `play-v29` = playVersion("29").settings(crossCompileScala3) lazy val `play-v30` = playVersion("30").settings(crossCompileScala3) @@ -77,7 +72,6 @@ lazy val `play-v30` = playVersion("30").settings(crossCompileScala3) lazy val `play-secret-rotation-root` = (project in file(".")) .aggregate( core, - `play-v27`, `play-v28`, `play-v29`, `play-v30`, diff --git a/play/play-v27/RotatingSecretComponents.scala b/play/play-v27/RotatingSecretComponents.scala deleted file mode 100644 index 1995b5a..0000000 --- a/play/play-v27/RotatingSecretComponents.scala +++ /dev/null @@ -1,62 +0,0 @@ -package com.gu.play.secretrotation - -import java.time.Clock -import java.time.Clock.systemUTC - -import play.api.http._ -import play.api.mvc._ -import play.api.mvc.request.{DefaultRequestFactory, RequestFactory} -import play.api.{BuiltInComponentsFromContext, Configuration} - - -trait RotatingSecretComponents extends BuiltInComponentsFromContext { - - val secretStateSupplier: SnapshotProvider - - override def configuration: Configuration = { - val nonRotatingSecretOnlyUsedToSatisfyConfigChecks = secretStateSupplier.snapshot().secrets.active - - super.configuration ++ Configuration("play.http.secret.key" -> nonRotatingSecretOnlyUsedToSatisfyConfigChecks) - } - - override lazy val requestFactory: RequestFactory = - RotatingSecretComponents.requestFactoryFor(secretStateSupplier, httpConfiguration) -} - -object RotatingSecretComponents { - def requestFactoryFor(snapshotProvider: SnapshotProvider, hc: HttpConfiguration): RequestFactory = - new DefaultRequestFactory( - new DefaultCookieHeaderEncoding(hc.cookies), - new RotatingKeySessionCookieBaker(hc.session, snapshotProvider), - new RotatingKeyFlashCookieBaker(hc.flash, snapshotProvider) - ) - - - trait RotatingSecretCookieCodec extends CookieDataCodec { - val snapshotProvider: SnapshotProvider - val jwtConfiguration: JWTConfiguration - - implicit val c: Clock = systemUTC() - - private def jwtCodecFor(secret: String) = DefaultJWTCookieDataCodec(SecretConfiguration(secret), jwtConfiguration) - - override def encode(data: Map[String, String]): String = - jwtCodecFor(snapshotProvider.snapshot().secrets.active).encode(data) - - override def decode(data: String): Map[String, String] = { - snapshotProvider.snapshot().decode[Map[String, String]](jwtCodecFor(_).decode(data), _.nonEmpty).getOrElse(Map.empty) - } - } - - class RotatingKeySessionCookieBaker( - val config: SessionConfiguration, - val snapshotProvider: SnapshotProvider) extends SessionCookieBaker with RotatingSecretCookieCodec { - override val jwtConfiguration: JWTConfiguration = config.jwt - } - - class RotatingKeyFlashCookieBaker( - val config: FlashConfiguration, - val snapshotProvider: SnapshotProvider) extends FlashCookieBaker with RotatingSecretCookieCodec { - override val jwtConfiguration: JWTConfiguration = config.jwt - } -} \ No newline at end of file diff --git a/play/play-v28/RotatingSecretComponents.scala b/play/play-v28/RotatingSecretComponents.scala deleted file mode 120000 index a060dc1..0000000 --- a/play/play-v28/RotatingSecretComponents.scala +++ /dev/null @@ -1 +0,0 @@ -../play-v27/RotatingSecretComponents.scala \ No newline at end of file diff --git a/play/play-v28/RotatingSecretComponents.scala b/play/play-v28/RotatingSecretComponents.scala new file mode 100644 index 0000000..a68e70e --- /dev/null +++ b/play/play-v28/RotatingSecretComponents.scala @@ -0,0 +1,62 @@ +package com.gu.play.secretrotation + +import java.time.Clock +import java.time.Clock.systemUTC + +import play.api.http._ +import play.api.mvc._ +import play.api.mvc.request.{DefaultRequestFactory, RequestFactory} +import play.api.{BuiltInComponentsFromContext, Configuration} + + +trait RotatingSecretComponents extends BuiltInComponentsFromContext { + + val secretStateSupplier: SnapshotProvider + + override def configuration: Configuration = { + val nonRotatingSecretOnlyUsedToSatisfyConfigChecks = secretStateSupplier.snapshot().secrets.active + + super.configuration.withFallback(Configuration("play.http.secret.key" -> nonRotatingSecretOnlyUsedToSatisfyConfigChecks)) + } + + override lazy val requestFactory: RequestFactory = + RotatingSecretComponents.requestFactoryFor(secretStateSupplier, httpConfiguration) +} + +object RotatingSecretComponents { + def requestFactoryFor(snapshotProvider: SnapshotProvider, hc: HttpConfiguration): RequestFactory = + new DefaultRequestFactory( + new DefaultCookieHeaderEncoding(hc.cookies), + new RotatingKeySessionCookieBaker(hc.session, snapshotProvider), + new RotatingKeyFlashCookieBaker(hc.flash, snapshotProvider) + ) + + + trait RotatingSecretCookieCodec extends CookieDataCodec { + val snapshotProvider: SnapshotProvider + val jwtConfiguration: JWTConfiguration + + implicit val c: Clock = systemUTC() + + private def jwtCodecFor(secret: String) = DefaultJWTCookieDataCodec(SecretConfiguration(secret), jwtConfiguration) + + override def encode(data: Map[String, String]): String = + jwtCodecFor(snapshotProvider.snapshot().secrets.active).encode(data) + + override def decode(data: String): Map[String, String] = { + snapshotProvider.snapshot().decode[Map[String, String]](jwtCodecFor(_).decode(data), _.nonEmpty).getOrElse(Map.empty) + } + } + + class RotatingKeySessionCookieBaker( + val config: SessionConfiguration, + val snapshotProvider: SnapshotProvider) extends SessionCookieBaker with RotatingSecretCookieCodec { + override val jwtConfiguration: JWTConfiguration = config.jwt + } + + class RotatingKeyFlashCookieBaker( + val config: FlashConfiguration, + val snapshotProvider: SnapshotProvider) extends FlashCookieBaker with RotatingSecretCookieCodec { + override val jwtConfiguration: JWTConfiguration = config.jwt + } +} \ No newline at end of file diff --git a/play/play-v29/RotatingSecretComponents.scala b/play/play-v29/RotatingSecretComponents.scala index a060dc1..23e49fb 120000 --- a/play/play-v29/RotatingSecretComponents.scala +++ b/play/play-v29/RotatingSecretComponents.scala @@ -1 +1 @@ -../play-v27/RotatingSecretComponents.scala \ No newline at end of file +../play-v28/RotatingSecretComponents.scala \ No newline at end of file diff --git a/play/play-v30/RotatingSecretComponents.scala b/play/play-v30/RotatingSecretComponents.scala index a060dc1..23e49fb 120000 --- a/play/play-v30/RotatingSecretComponents.scala +++ b/play/play-v30/RotatingSecretComponents.scala @@ -1 +1 @@ -../play-v27/RotatingSecretComponents.scala \ No newline at end of file +../play-v28/RotatingSecretComponents.scala \ No newline at end of file