Skip to content

Commit

Permalink
BDOG-3023 remove isAssistedDigital feature flag in config (#525)
Browse files Browse the repository at this point in the history
* BDOG-3023 remove isAssistedDigital feature flag in config

* BDOG-3023 add test only filter to simulate admin proxy routing

* BDOG-3023 update KeepAliveController and views to derive isAssistedDigital flag

* BDOG-3023 remove unused SessionExpiredView

* BDOG-3023 run formatter and update README

* fix A11y tests

* remove comment

* BDOG-3023 address lookup frontend callback url logic

* add flag to test only page

* fix error template styling

* fix tests

---------

Co-authored-by: “Taiwo-AB” <[email protected]>
Co-authored-by: mywyau <[email protected]>
  • Loading branch information
3 people authored May 22, 2024
1 parent 9914309 commit 14054de
Show file tree
Hide file tree
Showing 132 changed files with 1,130 additions and 781 deletions.
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Business travellers carrying commercial goods for both import or export.

## How to start the service locally

`sbt run` This will only start the service as standalone but unable to interact with any other services including Backend and DataBase
`sbt run` This will only start the service as standalone but unable to interact with any other services including Backend and Database

To load all related services:

Expand Down Expand Up @@ -57,6 +57,17 @@ To run the tests locally, simply run:
sbt clean A11y/test
```

## How to enable 'Admin Mode'

This service is built to accept traffic from the `admin.tax.service.gov.uk` domain as well for assisted digital journeys.
The service can detect where the traffic has come from by inspecting the `x-forwarded-host` header, this is done in
`auth/StrideAuthAction.scala` - this will change some content on some of the pages and also alters the payment journey.

When testing locally, you can enable a filter which will add a header to each request to simulate it coming from the admin domain.

This can be done by updating the `adminJourneyFilter.enabled` flag in application.conf to be `true` or alternatively passing it in as
a system property e.g. `sbt run -DadminJourneyFilter.enabled=true`

## License

This code is open source software licensed under the [Apache 2.0 License]("http://www.apache.org/licenses/LICENSE-2.0.html").
55 changes: 36 additions & 19 deletions a11y/FrontendAccessibilitySpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ import org.scalacheck.Arbitrary
import play.api.data.Form
import play.api.data.Forms._
import play.twirl.api.Html
import uk.gov.hmrc.merchandiseinbaggage.auth.AuthRequest
import uk.gov.hmrc.merchandiseinbaggage.config.AppConfig
import uk.gov.hmrc.merchandiseinbaggage.controllers.{DeclarationGoodsRequest, DeclarationJourneyRequest}
import uk.gov.hmrc.merchandiseinbaggage.forms._
import uk.gov.hmrc.merchandiseinbaggage.model.api._
import uk.gov.hmrc.merchandiseinbaggage.model.api.calculation.CalculationResults
Expand All @@ -34,27 +36,43 @@ class FrontendAccessibilitySpec extends AutomaticAccessibilitySpec {
private val optionEmailForm: Form[Option[Email]] = EnterEmailForm.optionalForm
private val booleanForm: Form[Boolean] = Form("value" -> boolean)
private val stringForm: Form[String] = Form("value" -> text)
private val authRequest = AuthRequest(
request = fakeRequest,
credentials = None,
isAssistedDigital = false
)
private val declarationJourneyRequest = new DeclarationJourneyRequest(
declarationJourney = completedDeclarationJourney,
request = authRequest
)
private val declarationGoodsRequest = new DeclarationGoodsRequest(
declarationJourneyRequest = declarationJourneyRequest,
goodsEntry = completedImportGoods
)

implicit val arbHtml: Arbitrary[Html] = fixed(Html(""))
implicit val arbForm: Arbitrary[Form[_]] = fixed(booleanForm)
implicit val arbString: Arbitrary[String] = fixed("http://something")
implicit val arbFormString: Arbitrary[Form[String]] = fixed(stringForm)
implicit val arbAppConfig: Arbitrary[AppConfig] = fixed(appConfig)
implicit val arbEnterEmailForm: Arbitrary[Form[Email]] = fixed(emailForm)
implicit val arbEnterOptionEmailForm: Arbitrary[Form[Option[Email]]] = fixed(optionEmailForm)
implicit val arbDeclarationType: Arbitrary[DeclarationType] = fixed(DeclarationType.Import)
implicit val arbGoodsDestination: Arbitrary[GoodsDestination] = fixed(GoodsDestinations.GreatBritain)
implicit val arbDeclaration: Arbitrary[Declaration] = fixed(declaration)
implicit val arbJourneyType: Arbitrary[JourneyType] = fixed(JourneyTypes.New)
implicit val arbCheckYourAnswersImport: Arbitrary[CalculationResults] = fixed(aCalculationResultsWithNoTax)
implicit val arbCheckYourAnswersAmendExport: Arbitrary[Amendment] = fixed(aAmendment)
implicit val arbCheckYourAnswersExport: Arbitrary[YesNo] = fixed(YesNo.Yes)
implicit val arbJourneyDetailsEntry: Arbitrary[Form[JourneyDetailsEntry]] = fixed(
implicit val arbHtml: Arbitrary[Html] = fixed(Html(""))
implicit val arbForm: Arbitrary[Form[_]] = fixed(booleanForm)
implicit val arbString: Arbitrary[String] = fixed("http://something")
implicit val arbFormString: Arbitrary[Form[String]] = fixed(stringForm)
implicit val arbAppConfig: Arbitrary[AppConfig] = fixed(appConfig)
implicit val arbEnterEmailForm: Arbitrary[Form[Email]] = fixed(emailForm)
implicit val arbEnterOptionEmailForm: Arbitrary[Form[Option[Email]]] = fixed(optionEmailForm)
implicit val arbDeclarationType: Arbitrary[DeclarationType] = fixed(DeclarationType.Import)
implicit val arbGoodsDestination: Arbitrary[GoodsDestination] = fixed(GoodsDestinations.GreatBritain)
implicit val arbDeclaration: Arbitrary[Declaration] = fixed(declaration)
implicit val arbJourneyType: Arbitrary[JourneyType] = fixed(JourneyTypes.New)
implicit val arbCheckYourAnswersImport: Arbitrary[CalculationResults] = fixed(aCalculationResultsWithNoTax)
implicit val arbCheckYourAnswersAmendExport: Arbitrary[Amendment] = fixed(aAmendment)
implicit val arbCheckYourAnswersExport: Arbitrary[YesNo] = fixed(YesNo.Yes)
implicit val arbJourneyDetailsEntry: Arbitrary[Form[JourneyDetailsEntry]] = fixed(
JourneyDetailsForm.form(DeclarationType.Import, journeyDate)
)
implicit val arbPreviousDeclarationDetails: Arbitrary[ThresholdAllowance] = fixed(aThresholdAllowance)
implicit val arbPurchaseDetailsExport: Arbitrary[Form[PurchaseDetailsInput]] = fixed(PurchaseDetailsForm.form)
implicit val arbTravellerDetails: Arbitrary[Form[Name]] = fixed(TravellerDetailsForm.form)
implicit val arbPreviousDeclarationDetails: Arbitrary[ThresholdAllowance] = fixed(aThresholdAllowance)
implicit val arbPurchaseDetailsExport: Arbitrary[Form[PurchaseDetailsInput]] = fixed(PurchaseDetailsForm.form)
implicit val arbTravellerDetails: Arbitrary[Form[Name]] = fixed(TravellerDetailsForm.form)
implicit val arbAuthRequest: Arbitrary[AuthRequest[_]] = fixed(authRequest)
implicit val arbDeclarationJourneyRequest: Arbitrary[DeclarationJourneyRequest[_]] = fixed(declarationJourneyRequest)
implicit val arbDeclarationGoodsRequest: Arbitrary[DeclarationGoodsRequest[_]] = fixed(declarationGoodsRequest)

val viewPackageName = "uk.gov.hmrc.merchandiseinbaggage.views.html"

Expand Down Expand Up @@ -98,7 +116,6 @@ class FrontendAccessibilitySpec extends AutomaticAccessibilitySpec {
case reviewGoodsView: ReviewGoodsView => render(reviewGoodsView)
case searchGoodsCountryView: SearchGoodsCountryView => render(searchGoodsCountryView)
case serviceTimeoutView: ServiceTimeoutView => render(serviceTimeoutView)
case sessionExpiredView: SessionExpiredView => render(sessionExpiredView)
case testOnlyDeclarationJourneyPage: TestOnlyDeclarationJourneyPage => render(testOnlyDeclarationJourneyPage)
case travellerDetailsPage: TravellerDetailsPage => render(travellerDetailsPage)
case valueWeightOfGoodsView: ValueWeightOfGoodsView => render(valueWeightOfGoodsView)
Expand Down
6 changes: 5 additions & 1 deletion app/uk/gov/hmrc/merchandiseinbaggage/auth/AuthRequest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,8 @@ package uk.gov.hmrc.merchandiseinbaggage.auth
import play.api.mvc.{Request, WrappedRequest}
import uk.gov.hmrc.auth.core.retrieve.Credentials

case class AuthRequest[A](request: Request[A], credentials: Option[Credentials]) extends WrappedRequest[A](request)
case class AuthRequest[A](
request: Request[A],
credentials: Option[Credentials],
isAssistedDigital: Boolean
) extends WrappedRequest[A](request)
47 changes: 31 additions & 16 deletions app/uk/gov/hmrc/merchandiseinbaggage/auth/StrideAuthAction.scala
Original file line number Diff line number Diff line change
Expand Up @@ -59,23 +59,38 @@ class StrideAuthAction @Inject() (
toStrideLogin(uri)
}

authorised(AuthProviders(PrivilegedApplication))
.retrieve(credentials and allEnrolments) { case creds ~ enrolments =>
if (hasRequiredRoles(enrolments)) {
block(AuthRequest(request, creds))
} else {
Future successful Unauthorized("Insufficient Roles")
// This service handles traffic from the public internet as well as the stride domain.
// Traffic from the stride domain can be identified by the x-forwarded-host header.
// In the case that it's not from the stride domain, we don't need stride auth, instead
// we invoke the block with an AuthRequest with no credentials and the isAssistedDigital
// flag set to false

val isFromAdminDomain: Boolean =
request.headers
.get("x-forwarded-host")
.exists(host => host.startsWith("admin") || host.startsWith("test-admin"))

if (!isFromAdminDomain) {
block(AuthRequest(request, credentials = None, isAssistedDigital = false))
} else {
authorised(AuthProviders(PrivilegedApplication))
.retrieve(credentials and allEnrolments) { case creds ~ enrolments =>
if (hasRequiredRoles(enrolments)) {
block(AuthRequest(request, creds, isAssistedDigital = true))
} else {
Future successful Unauthorized("Insufficient Roles")
}
}
}
.recover {
case e: NoActiveSession =>
redirectToStrideLogin(e.getMessage)
case e: InternalError =>
redirectToStrideLogin(e.getMessage)
case e: AuthorisationException =>
logger.warn(s"User is forbidden because of ${e.reason}, $e")
Forbidden
}
.recover {
case e: NoActiveSession =>
redirectToStrideLogin(e.getMessage)
case e: InternalError =>
redirectToStrideLogin(e.getMessage)
case e: AuthorisationException =>
logger.warn(s"User is forbidden because of ${e.reason}, $e")
Forbidden
}
}
}

private def hasRequiredRoles(enrolments: Enrolments): Boolean = {
Expand Down
20 changes: 11 additions & 9 deletions app/uk/gov/hmrc/merchandiseinbaggage/config/AppConfig.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,18 @@
package uk.gov.hmrc.merchandiseinbaggage.config

import com.google.inject.Inject
import com.typesafe.config.ConfigFactory
import play.api.{Configuration, Environment}
import uk.gov.hmrc.merchandiseinbaggage.model.api.tpspayments.TpsNavigation
import uk.gov.hmrc.play.bootstrap.config.ServicesConfig

import javax.inject.Singleton

@Singleton
class AppConfig @Inject() (val config: Configuration, val env: Environment, servicesConfig: ServicesConfig)()
extends IsAssistedDigitalConfiguration {
class AppConfig @Inject() (
val config: Configuration,
val env: Environment,
servicesConfig: ServicesConfig
) {

private val serviceIdentifier = "mib"

Expand Down Expand Up @@ -62,11 +64,11 @@ class AppConfig @Inject() (val config: Configuration, val env: Environment, serv
lazy val tpsPaymentsBackendUrl: String = servicesConfig.baseUrl("tps-payments-backend")
lazy val merchandiseInBaggageUrl: String = servicesConfig.baseUrl("merchandise-in-baggage")
lazy val addressLookupFrontendUrl: String = servicesConfig.baseUrl("address-lookup-frontend")
lazy val addressLookupCallbackUrl: String =
config.get[String]("microservice.services.address-lookup-frontend.callback")
}

trait IsAssistedDigitalConfiguration {
// to avoid re writing the codebase, need to improve in the future to allow injection
lazy val isAssistedDigital: Boolean = ConfigFactory.load().getBoolean("assistedDigital")
def addressLookupCallbackUrl(isAssistedDigital: Boolean): String =
if (isAssistedDigital) {
config.get[String]("microservice.services.address-lookup-frontend.adminCallback")
} else {
config.get[String]("microservice.services.address-lookup-frontend.callback")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,16 @@ import scala.concurrent.{ExecutionContext, Future}
@Singleton
class AddressLookupFrontendConnector @Inject() (appConfig: AppConfig, http: HttpClient) {

private val baseUrl = appConfig.addressLookupFrontendUrl
private val callback = appConfig.addressLookupCallbackUrl
private val baseUrl = appConfig.addressLookupFrontendUrl

private lazy val initJourneyUrl = s"$baseUrl/api/v2/init"
private def confirmJourneyUrl(id: String) = s"$baseUrl/api/confirmed?id=$id"

def initJourney(call: Call)(implicit hc: HeaderCarrier, ec: ExecutionContext): Future[String] = {
def initJourney(call: Call, isAssistedDigital: Boolean)(implicit
hc: HeaderCarrier,
ec: ExecutionContext
): Future[String] = {
val callback = appConfig.addressLookupCallbackUrl(isAssistedDigital)
val addressConfig = Json.toJson(configAddressLookup(s"$callback${call.url}"))

http.POST[JsValue, HttpResponse](initJourneyUrl, addressConfig) map { response =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,18 @@ import javax.inject.{Inject, Singleton}
import play.api.mvc.{Action, AnyContent, MessagesControllerComponents}
import uk.gov.hmrc.merchandiseinbaggage.config.AppConfig
import uk.gov.hmrc.merchandiseinbaggage.views.html.CannotAccessPageView
import uk.gov.hmrc.play.bootstrap.frontend.controller.FrontendBaseController

import scala.concurrent.ExecutionContext

@Singleton
class CannotAccessPageController @Inject() (
override val controllerComponents: MessagesControllerComponents,
actionProvider: DeclarationJourneyActionProvider,
view: CannotAccessPageView
)(implicit val ec: ExecutionContext, appConfig: AppConfig)
extends FrontendBaseController {
extends DeclarationJourneyController {

def onPageLoad(): Action[AnyContent] = Action { implicit request =>
val onPageLoad: Action[AnyContent] = actionProvider.journeyAction { implicit request =>
Ok(view())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class CheckYourAnswersAmendHandler @Inject() (

def onPageLoad(declarationJourney: DeclarationJourney, amendment: Amendment, isAgent: YesNo)(implicit
hc: HeaderCarrier,
request: Request[_],
request: DeclarationJourneyRequest[_],
messages: Messages
): Future[Result] =
(for {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package uk.gov.hmrc.merchandiseinbaggage.controllers

import javax.inject.{Inject, Singleton}
import play.api.mvc.{Action, AnyContent, MessagesControllerComponents}
import uk.gov.hmrc.merchandiseinbaggage.config.IsAssistedDigitalConfiguration
import uk.gov.hmrc.merchandiseinbaggage.controllers.DeclarationJourneyController.incompleteMessage
import uk.gov.hmrc.merchandiseinbaggage.model.api.JourneyTypes.{Amend, New}
import uk.gov.hmrc.merchandiseinbaggage.model.api.YesNo
Expand All @@ -36,8 +35,7 @@ class CheckYourAnswersController @Inject() (
amendHandler: CheckYourAnswersAmendHandler,
override val repo: DeclarationJourneyRepository
)(implicit ec: ExecutionContext)
extends IsAssistedDigitalConfiguration
with DeclarationJourneyUpdateController {
extends DeclarationJourneyUpdateController {

val onPageLoad: Action[AnyContent] = actionProvider.journeyAction.async { implicit request =>
import request.declarationJourney._
Expand All @@ -61,7 +59,7 @@ class CheckYourAnswersController @Inject() (
case New =>
request.declarationJourney.declarationIfRequiredAndComplete
.fold(actionProvider.invalidRequestF(incompleteMessage)) { declaration =>
if (isAssistedDigital) {
if (request.isAssistedDigital) {
newHandler.onSubmitTps(declaration.copy(lang = messages.lang.code))
} else {
newHandler.onSubmit(declaration.copy(lang = messages.lang.code))
Expand All @@ -70,7 +68,7 @@ class CheckYourAnswersController @Inject() (
case Amend =>
request.declarationJourney.amendmentIfRequiredAndComplete
.fold(actionProvider.invalidRequestF(incompleteMessage)) { amendment =>
if (isAssistedDigital) {
if (request.isAssistedDigital) {
amendHandler.onSubmitTps(
declarationId = request.declarationJourney.declarationId,
newAmendment = amendment.copy(lang = messages.lang.code)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class CheckYourAnswersNewHandler @Inject() (

def onPageLoad(declaration: Declaration, isAgent: YesNo)(implicit
hc: HeaderCarrier,
request: Request[_],
request: DeclarationJourneyRequest[_],
messages: Messages
): Future[Result] =
mibService.paymentCalculations(declaration.declarationGoods.goods, declaration.goodsDestination).map {
Expand Down
Loading

0 comments on commit 14054de

Please sign in to comment.