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

wrap admin and preview in pan-domain-authentication 🐼 #27012

Merged
merged 3 commits into from
May 2, 2024
Merged
Show file tree
Hide file tree
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
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")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0K

}

}
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
mxdvl marked this conversation as resolved.
Show resolved Hide resolved
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)
Comment on lines +59 to +61
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unroll please 🧵 👏

}

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

This file was deleted.

Loading
Loading