app/controllers/Emergency.scala (104 lines of code) (raw):

package controllers import com.amazonaws.services.simpleemail.AmazonSimpleEmailService import com.github.nscala_time.time.Imports._ import com.gu.pandomainauth.model.{AuthenticatedUser, CookieParseException, CookieSignatureInvalidException, User} import com.gu.pandomainauth.service.CookieUtils import com.gu.pandomainauth.{PanDomainAuthSettingsRefresher, PublicSettings} import play.api.mvc._ import services.NewCookieIssue import utils._ import scala.util.Random import scala.util.control.NonFatal class Emergency( loginPublicSettings: PublicSettings, deps: LoginControllerComponents, sesClient: AmazonSimpleEmailService, panDomainSettings: PanDomainAuthSettingsRefresher ) extends LoginController(deps, panDomainSettings) with Loggable { private val cookieLifetime = 1.day def reissueDisabled: Action[AnyContent] = Action { Ok(views.html.emergency.reissueDisabled()) } def reissue: Action[AnyContent] = EmergencySwitchIsOnAction { req => val reissueTopic = "Your login session has not been extended" (for { assymCookie <- req.cookies.find(_.name == panDomainSettings.settings.cookieSettings.cookieName) } 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 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) )).getOrElse { unauthorised("No existing login session found, unable to log you in.", reissueTopic) } } def requestCookieLink: Action[AnyContent] = EmergencySwitchIsOnAction { Ok(views.html.emergency.requestNewCookie()) } def sendCookieLink: Action[AnyContent] = EmergencySwitchIsOnAction { req => val tokenIssuedAt = DateTime.now().getMillis try { val emailPrefix = req.body.asFormUrlEncoded.get("email").head val emailAddress = s"$emailPrefix@guardian.co.uk" val token = Random.alphanumeric.take(20).mkString val cookieIssue = NewCookieIssue(token, emailAddress, tokenIssuedAt, false) try { deps.tokenDBService.createCookieIssue(cookieIssue) val ses = new SES(sesClient, config) ses.sendCookieEmail(token, emailAddress) Ok(views.html.emergency.emailSent()) } catch { case NonFatal(e) => InternalServerError(e.toString) } } catch { case NonFatal(_) => BadRequest("both first and last names must be submitted") } } def issueNewCookie(userToken: String): Action[AnyContent] = EmergencySwitchIsOnAction { def issueNewCookie(newCookieIssue: NewCookieIssue): Result = { deps.tokenDBService.expireCookieIssue(newCookieIssue) val expires = (DateTime.now() + cookieLifetime).getMillis val names = newCookieIssue.email.split("\\.") val firstName = names(0).capitalize val lastName = names(1).split("@")(0).capitalize val user = User(firstName, lastName, newCookieIssue.email, None) val newAuthUser = AuthenticatedUser(user, config.appName, Set(config.appName), expires, multiFactor = true) val authCookie = generateCookie(newAuthUser) Ok(views.html.emergency.reissueSuccess()).withCookies(authCookie) } val issueNewCookieTopic = "New cookie has not been created" val tenMinutesInMilliSeconds = 600000 val tokenOpt = deps.tokenDBService.getCookieIssueForUserToken(userToken) tokenOpt.map { case Left(error) => { log.warn(s"Error when reading entry with $userToken from dynamo. A new cookie will not be issued: $error") unauthorised("Checking your access token failed. You will not be issued with a new ", issueNewCookieTopic) } case Right(tokenEntry: NewCookieIssue) => { if (!tokenEntry.used) { val tokenAgeInMilliseconds = DateTime.now().getMillis - tokenEntry.requested if (tokenAgeInMilliseconds > tenMinutesInMilliSeconds) { log.warn(s"Attempted to use expired token: ${tokenEntry.id}") Unauthorized(views.html.emergency.newCookieFailure("Your link has expired. Could not create a new cookie")) } else { issueNewCookie(tokenEntry) } } else { log.warn(s"Attempted to use a used token: ${tokenEntry.id}") Unauthorized(views.html.emergency.newCookieFailure("Your link has already been been used")) } } }.getOrElse(Unauthorized("Token not found")) } private def unauthorised(message: String, topic: String): Result = { log.warn(message) Unauthorized(views.html.emergency.reissueFailure(message, topic)) } }