Skip to content

Commit

Permalink
Upgrade to Panda v7 - support key rotation
Browse files Browse the repository at this point in the history
This upgrades Panda from v5 to v7, allowing us to use key rotation as
introduced with guardian/pan-domain-authentication#150.

As login.gutools.co.uk is pretty special user of Panda the upgrade is slightly
more involved than other upgrades (eg guardian/atom-workshop#361):

* Panda v6:
  * guardian/pan-domain-authentication#152
    `CookieUtils.generateCookieData()` now communicates errors with
    `CookieResult` values containing `CookieIntegrityFailure`, rather than
    exceptions.
* Panda v7:
  * guardian/pan-domain-authentication#150 means
    that code shouldn't directly reference private or public keys anymore
    (eg do not reference `settings.signingKeyPair`). Instead, use
    `settings.signingAndVerification` or `publicSettings.verification`.
    Note also that `publicSettings.publicKey` was previously optional, and
    `publicSettings.verification` is not.
  • Loading branch information
rtyley committed Sep 19, 2024
1 parent 5409609 commit 881deca
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 45 deletions.
42 changes: 18 additions & 24 deletions app/AppComponents.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import com.gu.pandomainauth.{PanDomainAuthSettingsRefresher, PublicSettings}
import com.gu.pandomainauth.S3BucketLoader.forAwsSdkV1
import com.gu.pandomainauth.{PanDomainAuthSettingsRefresher, PublicSettings, S3BucketLoader, Settings}
import config.{AWS, LoginConfig, Switches}
import controllers._
import play.api.ApplicationLoader.Context
Expand All @@ -11,29 +12,22 @@ class AppComponents(context: Context) extends LoginControllerComponents(context,

override val switches = new Switches(config, aws.s3Client)

private lazy val panDomainSettings: PanDomainAuthSettingsRefresher =
new PanDomainAuthSettingsRefresher(
domain = config.domain,
system = "login",
bucketName = config.pandaAuthBucket,
settingsFileKey = s"${config.domain}.settings",
s3Client = aws.s3Client
)

private lazy val desktopPanDomainSettings: PanDomainAuthSettingsRefresher = {
new PanDomainAuthSettingsRefresher(
domain = config.desktopDomain,
system = "login-desktop",
bucketName = config.pandaAuthBucket,
settingsFileKey = s"${config.desktopDomain}.settings",
s3Client = aws.s3Client
)
}

val loginPublicSettings = new PublicSettings(
settingsFileKey = s"${config.domain}.settings.public",
bucketName = config.pandaAuthBucket,
s3Client = aws.s3Client
private val s3BucketLoader: S3BucketLoader = forAwsSdkV1(aws.s3Client, "pan-domain-auth-settings")

private lazy val panDomainSettings: PanDomainAuthSettingsRefresher = PanDomainAuthSettingsRefresher(
domain = config.domain,
system = "login",
s3BucketLoader
)

private lazy val desktopPanDomainSettings: PanDomainAuthSettingsRefresher = PanDomainAuthSettingsRefresher(
domain = config.desktopDomain,
system = "login-desktop",
s3BucketLoader
)

val loginPublicSettings: PublicSettings = PublicSettings(
new Settings.Loader(s3BucketLoader, s"${config.domain}.settings.public")
)

loginPublicSettings.start()
Expand Down
4 changes: 2 additions & 2 deletions app/controllers/DesktopLogin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class DesktopLogin(
request.headers.get(AUTHORIZATION) match {
case Some(s"GU-Desktop-Panda $token") =>
PanDomain.authStatus(token,
publicKey = panDomainSettings.settings.signingKeyPair.getPublic,
verification = panDomainSettings.settings.signingAndVerification,
validateUser = PanDomain.guardianValidation,
apiGracePeriod = 9.hours.toMillis,
system = panDomainSettings.system,
Expand Down Expand Up @@ -64,7 +64,7 @@ class DesktopLogin(


if (validateUser(authedUserData)) {
val token = CookieUtils.generateCookieData(authedUserData, panDomainSettings.settings.signingKeyPair.getPrivate)
val token = CookieUtils.generateCookieData(authedUserData, panDomainSettings.settings.signingAndVerification)
Redirect(s"gu-panda://desktop?token=${URLEncoder.encode(token, "UTF-8")}&stage=${deps.config.stage.toLowerCase}")
.withSession(session = request.session - ANTI_FORGERY_KEY - LOGIN_ORIGIN_KEY)
} else {
Expand Down
25 changes: 7 additions & 18 deletions app/controllers/Emergency.scala
Original file line number Diff line number Diff line change
Expand Up @@ -30,27 +30,16 @@ class Emergency(
val reissueTopic = "Your login session has not been extended"

(for {
publicKey <- loginPublicSettings.publicKey
assymCookie <- req.cookies.find(_.name == panDomainSettings.settings.cookieSettings.cookieName)
} yield {
try {
val authenticatedUser = CookieUtils.parseCookieData(assymCookie.value, publicKey)
} yield CookieUtils.parseCookieData(assymCookie.value, loginPublicSettings.verification).fold(
tokenIntegrityFailure =>
unauthorised(s"Invalid existing session, could not log you in: $tokenIntegrityFailure", reissueTopic)
, authenticatedUser =>
if (validateUser(authenticatedUser)) {
val expires = (DateTime.now() + cookieLifetime).getMillis
val newAuthUser = authenticatedUser.copy(expires = expires)
val authCookie = generateCookie(newAuthUser)
val authCookie = generateCookie(authenticatedUser.copy(expires = (DateTime.now() + cookieLifetime).getMillis))
Ok(views.html.emergency.reissueSuccess()).withCookies(authCookie)
} else {
unauthorised("Only Guardian email addresses with two-factor auth are supported.", reissueTopic)
}
}
catch {
case _: CookieSignatureInvalidException =>
unauthorised("Invalid existing session, could not log you in.", reissueTopic)
case _: CookieParseException =>
unauthorised("Could not refresh existing session due to a corrupted cookie.", reissueTopic)
}
}).getOrElse {
} else unauthorised("Only Guardian email addresses with two-factor auth are supported.", reissueTopic)
)).getOrElse {
unauthorised("No existing login session found, unable to log you in.", reissueTopic)
}
}
Expand Down
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ resolvers ++= Resolver.sonatypeOssRepos("releases")
libraryDependencies ++= Seq(
jdbc,
ws,
"com.gu" %% "pan-domain-auth-play_3-0" % "5.0.0",
"com.gu" %% "pan-domain-auth-play_3-0" % "7.0.0",
"com.gu.play-secret-rotation" %% "aws-parameterstore-sdk-v1" % "7.1.1",
"com.gu.play-secret-rotation" %% "play-v30" % "7.1.1",
"com.amazonaws" % "aws-java-sdk-ec2" % awsSdkVersion,
Expand Down

0 comments on commit 881deca

Please sign in to comment.