Skip to content

Commit

Permalink
change authentication for preview and admin to `pan-domain-authen…
Browse files Browse the repository at this point in the history
…tication` from plain Google auth
  • Loading branch information
twrichards committed Apr 25, 2024
1 parent 2d0ea08 commit 2546f48
Show file tree
Hide file tree
Showing 30 changed files with 224 additions and 600 deletions.
2 changes: 1 addition & 1 deletion .prout.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
}
},
"ADMIN-PROD": {
"url": "https://frontend.gutools.co.uk/login",
"url": "https://frontend.gutools.co.uk/_healthcheck",
"overdue": "30M",
"messages": {
"seen": "prout/seen.md"
Expand Down
39 changes: 36 additions & 3 deletions admin/app/AppLoader.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ import conf.switches.SwitchboardLifecycle
import conf.CachedHealthCheckLifeCycle
import controllers.{AdminControllers, HealthCheck}
import _root_.dfp.DfpDataCacheLifecycle
import com.amazonaws.regions.Regions
import com.amazonaws.services.s3.AmazonS3ClientBuilder
import org.apache.pekko.actor.{ActorSystem => PekkoActorSystem}
import concurrent.BlockingOperations
import contentapi.{CapiHttpClient, ContentApiClient, HttpClient}
import http.{AdminFilters, AdminHttpErrorHandler, CommonGzipFilter}
import http.{AdminHttpErrorHandler, CommonGzipFilter, Filters, GuardianAuthWithExemptions, routes}
import dev.DevAssetsController
import jobs._
import model.{AdminLifecycle, ApplicationIdentity}
Expand All @@ -25,6 +27,7 @@ import play.api.i18n.I18nComponents
import play.api.libs.ws.WSClient
import services.{ParameterStoreService, _}
import router.Routes
import conf.Configuration.aws.mandatoryCredentials

import scala.concurrent.ExecutionContext

Expand Down Expand Up @@ -76,6 +79,35 @@ trait AdminServices extends I18nComponents {

trait AppComponents extends FrontendComponents with AdminControllers with AdminServices {

private lazy val s3Client = AmazonS3ClientBuilder
.standard()
.withRegion(Regions.EU_WEST_1)
.withCredentials(
mandatoryCredentials,
)
.build()

lazy val auth = new GuardianAuthWithExemptions(
controllerComponents,
wsClient,
toolsDomainPrefix = "frontend",
oauthCallbackPath = routes.GuardianAuthWithExemptions.oauthCallback.path,
s3Client,
system = "frontend-admin",
extraDoNotAuthenticatePathPrefixes = Seq(
"/deploys", //not authenticated so it can be accessed by Prout to determine which builds have been deployed
"/deploy", //not authenticated so it can be accessed by Riff-Raff to notify about a new build being deployed
// Date: 06 July 2021
// Author: Pascal
// Added as part of posing the ground for the interactive migration.
// It should be removed when the Interactives migration is complete, meaning when we no longer need the routes
// POST /interactive-librarian/live-presser/*path
// POST /interactive-librarian/read-clean-write/*path
// in [admin].
"/interactive-librarian/",
),
)

lazy val healthCheck = wire[HealthCheck]
lazy val devAssetsController = wire[DevAssetsController]
lazy val logbackOperationsPool = wire[LogbackOperationsPool]
Expand All @@ -88,7 +120,6 @@ trait AppComponents extends FrontendComponents with AdminControllers with AdminS
wire[SurgingContentAgentLifecycle],
wire[DfpAgentLifecycle],
wire[DfpDataCacheLifecycle],
wire[CachedHealthCheckLifeCycle],
wire[CommercialDfpReportingLifecycle],
)

Expand All @@ -103,6 +134,8 @@ trait AppComponents extends FrontendComponents with AdminControllers with AdminS

def pekkoActorSystem: PekkoActorSystem

override lazy val httpFilters: Seq[EssentialFilter] =
auth.Filter :: Filters.common(frontend.admin.BuildInfo) ++ wire[CommonGzipFilter].filters

override lazy val httpErrorHandler: HttpErrorHandler = wire[AdminHttpErrorHandler]
override lazy val httpFilters: Seq[EssentialFilter] = wire[CommonGzipFilter].filters ++ wire[AdminFilters].filters
}
3 changes: 2 additions & 1 deletion admin/app/controllers/AdminControllers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import controllers.admin._
import controllers.admin.commercial._
import controllers.cache.{ImageDecacheController, PageDecacheController}
import dfp._
import http.GuardianAuthWithExemptions
import model.ApplicationContext
import play.api.http.HttpConfiguration
import play.api.libs.ws.WSClient
Expand Down Expand Up @@ -37,8 +38,8 @@ trait AdminControllers {
def placementService: PlacementService
def dfpApi: DfpApi
def parameterStoreService: ParameterStoreService
def auth: GuardianAuthWithExemptions

lazy val oAuthLoginController = wire[OAuthLoginAdminController]
lazy val uncachedWebAssets = wire[UncachedWebAssets]
lazy val uncachedAssets = wire[UncachedAssets]
lazy val adminIndexController = wire[AdminIndexController]
Expand Down
17 changes: 8 additions & 9 deletions admin/app/controllers/HealthCheck.scala
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package controllers

import conf.{AllGoodCachedHealthCheck, NeverExpiresSingleHealthCheck}
import play.api.libs.ws.WSClient
import play.api.mvc.ControllerComponents
import play.api.mvc.{Action, AnyContent, BaseController, ControllerComponents}

import scala.concurrent.ExecutionContext
class HealthCheck(val controllerComponents: ControllerComponents) extends BaseController {

class HealthCheck(wsClient: WSClient, val controllerComponents: ControllerComponents)(implicit
executionContext: ExecutionContext,
) extends AllGoodCachedHealthCheck(
NeverExpiresSingleHealthCheck("/login"),
)(wsClient, executionContext)
def healthCheck(): Action[AnyContent] =
Action {
Ok("OK")
}

}
17 changes: 0 additions & 17 deletions admin/app/controllers/admin/IndexController.scala
Original file line number Diff line number Diff line change
@@ -1,25 +1,8 @@
package controllers.admin

import com.gu.googleauth.AuthAction
import conf.AdminConfiguration
import model.{ApplicationContext, NoCache}
import play.api.http.HttpConfiguration
import play.api.mvc._

trait AdminAuthController {

def controllerComponents: ControllerComponents

case class AdminAuthAction(httpConfiguration: HttpConfiguration)
extends AuthAction(
conf
.GoogleAuth(None, httpConfiguration, AdminConfiguration.oauthCredentialsWithSingleCallBack(None))
.getConfigOrDie,
routes.OAuthLoginAdminController.login,
controllerComponents.parsers.default,
)(controllerComponents.executionContext)
}

class AdminIndexController(val controllerComponents: ControllerComponents)(implicit context: ApplicationContext)
extends BaseController {

Expand Down
34 changes: 0 additions & 34 deletions admin/app/controllers/admin/OAuthLoginAdminController.scala

This file was deleted.

12 changes: 9 additions & 3 deletions admin/app/controllers/admin/SwitchboardController.scala
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
package controllers.admin

import com.gu.googleauth.UserIdentity
import common._
import conf.Configuration
import conf.switches.Switches
import http.GuardianAuthWithExemptions
import model.{ApplicationContext, NoCache}
import play.api.mvc._
import services.SwitchNotification
import tools.Store

import scala.concurrent.Future

class SwitchboardController(pekkoAsync: PekkoAsync, val controllerComponents: ControllerComponents)(implicit
class SwitchboardController(
pekkoAsync: PekkoAsync,
auth: GuardianAuthWithExemptions,
val controllerComponents: ControllerComponents,
)(implicit
context: ApplicationContext,
) extends BaseController
with GuLogging
Expand Down Expand Up @@ -56,7 +60,9 @@ class SwitchboardController(pekkoAsync: PekkoAsync, val controllerComponents: Co
} else {
log.info("saving switchboard")

val requester: String = UserIdentity.fromRequest(request) map (_.fullName) getOrElse "unknown user (dev-build?)"
val requester: String =
auth.readAuthenticatedUser(request) map (authed => s"${authed.user.firstName} ${authed.user.lastName}",
) getOrElse "unknown user (dev-build?)"
val updates: Seq[String] = request.body.asFormUrlEncoded.map { params =>
Switches.all map { switch =>
switch.name + "=" + params.get(switch.name).map(v => "on").getOrElse("off")
Expand Down
21 changes: 7 additions & 14 deletions admin/app/controllers/cache/ImageDecacheController.scala
Original file line number Diff line number Diff line change
@@ -1,28 +1,22 @@
package controllers.cache

import java.net.URI
import java.util.UUID
import com.gu.googleauth.UserIdentity
import common.{GuLogging, ImplicitControllerExecutionContext}
import controllers.admin.AdminAuthController
import model.{ApplicationContext, NoCache}
import play.api.http.HttpConfiguration
import play.api.libs.ws.{WSClient, WSResponse}
import play.api.mvc.Security.AuthenticatedRequest
import play.api.mvc._

import java.net.URI
import java.util.UUID
import scala.concurrent.Future
import scala.concurrent.Future.successful

class ImageDecacheController(
wsClient: WSClient,
val controllerComponents: ControllerComponents,
val httpConfiguration: HttpConfiguration,
)(implicit context: ApplicationContext)
extends BaseController
with GuLogging
with ImplicitControllerExecutionContext
with AdminAuthController {
with ImplicitControllerExecutionContext {
import ImageDecacheController._

private val iGuim = """i.(guim|guimcode).co.uk/img/(static|media|uploads|sport)(/.*)""".r
Expand All @@ -34,9 +28,8 @@ class ImageDecacheController(
}

def decache(): Action[AnyContent] =
AdminAuthAction(httpConfiguration).async { implicit request =>
getSubmittedImage(request)
.map(new URI(_))
Action.async { implicit request =>
getSubmittedImageURI(request)
.map { imageUri =>
// here we limit the url to ones for which purging is supported
val originUrl: String = s"${imageUri.getHost}${imageUri.getPath}" match {
Expand Down Expand Up @@ -84,15 +77,15 @@ class ImageDecacheController(

}
.getOrElse(successful(BadRequest("No image submitted")))

}

private def getSubmittedImage(request: AuthenticatedRequest[AnyContent, UserIdentity]): Option[String] =
private def getSubmittedImageURI(request: Request[AnyContent]): Option[URI] =
request.body.asFormUrlEncoded
.getOrElse(Map.empty)
.get("url")
.flatMap(_.headOption)
.map(_.trim)
.map(new URI(_))

}

Expand Down
20 changes: 7 additions & 13 deletions admin/app/controllers/cache/PageDecacheController.scala
Original file line number Diff line number Diff line change
@@ -1,32 +1,24 @@
package controllers.cache

import java.net.URI
import com.gu.googleauth.UserIdentity
import common.{GuLogging, ImplicitControllerExecutionContext}
import controllers.admin.AdminAuthController
import model.{ApplicationContext, NoCache}
import org.apache.commons.codec.digest.DigestUtils
import play.api.http.HttpConfiguration
import play.api.libs.ws.WSClient
import play.api.mvc.Security.AuthenticatedRequest
import play.api.mvc._
import purge.{AjaxHost, CdnPurge, GuardianHost}

import scala.concurrent.Future
import scala.concurrent.Future.successful

case class PrePurgeTestResult(url: String, passed: Boolean)

class PageDecacheController(
wsClient: WSClient,
val controllerComponents: ControllerComponents,
val httpConfiguration: HttpConfiguration,
)(implicit
context: ApplicationContext,
) extends BaseController
with GuLogging
with ImplicitControllerExecutionContext
with AdminAuthController {
with ImplicitControllerExecutionContext {

def renderPageDecache(): Action[AnyContent] =
Action.async { implicit request =>
Expand All @@ -39,7 +31,7 @@ class PageDecacheController(
}

def decacheAjax(): Action[AnyContent] =
AdminAuthAction(httpConfiguration).async { implicit request =>
Action.async { implicit request =>
getSubmittedUrlPathMd5(request) match {
case Some(path) =>
CdnPurge.soft(wsClient, path, AjaxHost).map(message => NoCache(Ok(views.html.cache.ajaxDecache(message))))
Expand All @@ -48,7 +40,7 @@ class PageDecacheController(
}

def decachePage(): Action[AnyContent] =
AdminAuthAction(httpConfiguration).async { implicit request =>
Action.async { implicit request =>
getSubmittedUrlPathMd5(request) match {
case Some(md5Path) =>
CdnPurge
Expand All @@ -58,13 +50,15 @@ class PageDecacheController(
}
}

private def getSubmittedUrlPathMd5(request: AuthenticatedRequest[AnyContent, UserIdentity]): Option[String] = {
private def getSubmittedUrlPathMd5(request: Request[AnyContent]): Option[String] = {
request.body.asFormUrlEncoded
.getOrElse(Map.empty)
.get("url")
.flatMap(_.headOption)
.map(_.trim)
.map(url => DigestUtils.md5Hex(new URI(url).getPath))
.map(new URI(_))
.map(_.getPath)
.map(DigestUtils.md5Hex)
}

}
28 changes: 0 additions & 28 deletions admin/app/http/AdminFilters.scala

This file was deleted.

Loading

0 comments on commit 2546f48

Please sign in to comment.