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

[MTDSA-24840] EMA spike work in progress. #488

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
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
36 changes: 35 additions & 1 deletion app/config/BsasConfig.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@

package config

import play.api.Configuration
import com.typesafe.config.Config
import play.api.{ConfigLoader, Configuration}
import shared.config.FeatureSwitches
import uk.gov.hmrc.play.bootstrap.config.ServicesConfig

import javax.inject.{Inject, Singleton}
import scala.util.Try

/** Put API-specific config here...
*/
Expand All @@ -31,8 +33,40 @@ class BsasConfig @Inject() (config: ServicesConfig, configuration: Configuration

def featureSwitches: FeatureSwitches = BsasFeatureSwitches(featureSwitchConfig)

def secondaryAgentEndpointsAccessControlConfig: SecondaryAgentEndpointsAccessControlConfig =
configuration.get[SecondaryAgentEndpointsAccessControlConfig](s"api.secondary-agent-endpoints-access-control")

// V3 Trigger BSAS minimum dates
def v3TriggerForeignBsasMinimumTaxYear: String = config.getString("v3TriggerForeignBsasMinimumTaxYear")
def v3TriggerNonForeignBsasMinimumTaxYear: String = config.getString("v3TriggerNonForeignBsasMinimumTaxYear")

}

case class SecondaryAgentEndpointsAccessControlConfig(listBsas: Boolean,
triggerBsas: Boolean,
retrieveSelfEmploymentBsas: Boolean,
submitSelfEmploymentBsas: Boolean,
retrieveUKPropertyBsas: Boolean,
submitUKPropertyBsas: Boolean,
retrieveForeignPropertyBsas: Boolean,
submitForeignPropertyBsas: Boolean)

object SecondaryAgentEndpointsAccessControlConfig {

implicit val configLoader: ConfigLoader[SecondaryAgentEndpointsAccessControlConfig] = (rootConfig: Config, path: String) => {
val config = rootConfig.getConfig(path)
SecondaryAgentEndpointsAccessControlConfig(
getBooleanOrDefaultToFalse(config, "listBsas"),
getBooleanOrDefaultToFalse(config, "triggerBsas"),
getBooleanOrDefaultToFalse(config, "retrieveSelfEmploymentBsas"),
getBooleanOrDefaultToFalse(config, "submitSelfEmploymentBsas"),
getBooleanOrDefaultToFalse(config, "retrieveUKPropertyBsas"),
getBooleanOrDefaultToFalse(config, "submitUKPropertyBsas"),
getBooleanOrDefaultToFalse(config, "retrieveForeignPropertyBsas"),
getBooleanOrDefaultToFalse(config, "submitForeignPropertyBsas")
)
}

private def getBooleanOrDefaultToFalse(config: Config, endpoint: String): Boolean = Try(config.getBoolean(endpoint)).getOrElse(false)

}
16 changes: 10 additions & 6 deletions app/shared/controllers/AuthorisedController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,24 @@ abstract class AuthorisedController(cc: ControllerComponents)(implicit ec: Execu
val authService: EnrolmentsAuthService
val lookupService: MtdIdLookupService

def authorisedAction(nino: String): ActionBuilder[UserRequest, AnyContent] = new ActionBuilder[UserRequest, AnyContent] {
def authorisedAction(nino: String, secondaryAgentAccessAllowed : Boolean = false): ActionBuilder[UserRequest, AnyContent] = new ActionBuilder[UserRequest, AnyContent] {

override def parser: BodyParser[AnyContent] = cc.parsers.defaultBodyParser

override protected def executionContext: ExecutionContext = cc.executionContext

def predicate(mtdId: String): Predicate =
Enrolment("HMRC-MTD-IT")
.withIdentifier("MTDITID", mtdId)
.withDelegatedAuthRule("mtd-it-auth")
def predicate(mtdId: String): Predicate = {
Enrolment("HMRC-MTD-IT")
.withIdentifier("MTDITID", mtdId)
.withDelegatedAuthRule("mtd-it-auth") or
Enrolment("HMRC-MTD-IT-SECONDARY")
.withIdentifier("MTDITID", mtdId)
.withDelegatedAuthRule("mtd-it-auth-secondary")
}

def invokeBlockWithAuthCheck[A](mtdId: String, request: Request[A], block: UserRequest[A] => Future[Result])(implicit
headerCarrier: HeaderCarrier): Future[Result] = {
authService.authorised(predicate(mtdId)).flatMap[Result] {
authService.authorised(predicate(mtdId), secondaryAgentAccessAllowed).flatMap[Result] {
case Right(userDetails) => block(UserRequest(userDetails.copy(mtdId = mtdId), request))
case Left(mtdError) => errorResponse(mtdError)
}
Expand Down
49 changes: 26 additions & 23 deletions app/shared/services/EnrolmentsAuthService.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ import shared.utils.Logging
import uk.gov.hmrc.auth.core.AffinityGroup.{Agent, Individual, Organisation}
import uk.gov.hmrc.auth.core._
import uk.gov.hmrc.auth.core.authorise.Predicate
import uk.gov.hmrc.auth.core.retrieve.v2.Retrievals
import uk.gov.hmrc.auth.core.retrieve.v2.Retrievals._
import uk.gov.hmrc.auth.core.retrieve.~
import uk.gov.hmrc.http.HeaderCarrier

import javax.inject.{Inject, Singleton}
Expand All @@ -40,23 +40,29 @@ class EnrolmentsAuthService @Inject() (val connector: AuthConnector, val appConf

private def buildPredicate(predicate: Predicate): Predicate =
if (appConfig.confidenceLevelConfig.authValidationEnabled) {
predicate and ((Individual and ConfidenceLevel.L200) or Organisation or Agent)
predicate and ((Individual and ConfidenceLevel.L200) or Organisation or (Agent and Enrolment("HMRC-AS-AGENT")))
} else {
predicate
}

def authorised(predicate: Predicate)(implicit hc: HeaderCarrier, ec: ExecutionContext): Future[AuthOutcome] = {
def authorised(predicate: Predicate, secondaryAgentAccessAllowed: Boolean = false)(implicit
hc: HeaderCarrier,
ec: ExecutionContext): Future[AuthOutcome] = {
authFunction
.authorised(buildPredicate(predicate))
.retrieve(affinityGroup) {
case Some(Individual) => Future.successful(Right(UserDetails("", "Individual", None)))
case Some(Organisation) => Future.successful(Right(UserDetails("", "Organisation", None)))
case Some(Agent) =>
retrieveAgentDetails().map {
case arn @ Some(_) => Right(UserDetails("", "Agent", arn))
case None =>
logger.warn(s"[EnrolmentsAuthService][authorised] No AgentReferenceNumber defined on agent enrolment.")
Left(InternalError)
.retrieve(affinityGroup and authorisedEnrolments) {
case Some(Individual) ~ _ => Future.successful(Right(UserDetails("", "Individual", None)))
case Some(Organisation) ~ _ => Future.successful(Right(UserDetails("", "Organisation", None)))
case Some(Agent) ~ authorisedEnrolments =>
if (isSecondaryAgent(authorisedEnrolments) && !secondaryAgentAccessAllowed) {
Future.successful(Left(ClientOrAgentNotAuthorisedError))
} else {
retrieveAgentDetails(authorisedEnrolments) match {
case arn @ Some(_) => Future.successful(Right(UserDetails("", "Agent", arn)))
case None =>
logger.warn(s"[EnrolmentsAuthService][authorised] No AgentReferenceNumber defined on agent enrolment.")
Future.successful(Left(InternalError))
}
}
case _ =>
logger.warn(s"[EnrolmentsAuthService][authorised] Invalid AffinityGroup.")
Expand All @@ -71,18 +77,15 @@ class EnrolmentsAuthService @Inject() (val connector: AuthConnector, val appConf
}
}

private def retrieveAgentDetails()(implicit hc: HeaderCarrier, ec: ExecutionContext): Future[Option[String]] = {
def getAgentReferenceFrom(enrolments: Enrolments) =
enrolments
.getEnrolment("HMRC-AS-AGENT")
.flatMap(_.getIdentifier("AgentReferenceNumber"))
.map(_.value)
private def isSecondaryAgent(authorisedEnrolments: Enrolments): Boolean = {
authorisedEnrolments.getEnrolment("HMRC-MTD-IT-SECONDARY").isDefined
}

authFunction
.authorised(AffinityGroup.Agent and Enrolment("HMRC-AS-AGENT"))
.retrieve(Retrievals.authorisedEnrolments) { enrolments =>
Future.successful(getAgentReferenceFrom(enrolments))
}
private def retrieveAgentDetails(authorisedEnrolments: Enrolments): Option[String] = {
authorisedEnrolments
.getEnrolment("HMRC-AS-AGENT")
.flatMap(_.getIdentifier("AgentReferenceNumber"))
.map(_.value)
}

}
6 changes: 4 additions & 2 deletions app/v3/controllers/ListBsasController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package v3.controllers

import config.BsasConfig
import play.api.mvc.{Action, AnyContent, ControllerComponents}
import shared.config.AppConfig
import shared.controllers._
Expand All @@ -36,15 +37,16 @@ class ListBsasController @Inject() (val authService: EnrolmentsAuthService,
service: ListBsasService,
hateoasFactory: HateoasFactory,
cc: ControllerComponents,
val idGenerator: IdGenerator)(implicit ec: ExecutionContext, appConfig: AppConfig)
val idGenerator: IdGenerator,
val bsasConfig: BsasConfig)(implicit ec: ExecutionContext, appConfig: AppConfig)
extends AuthorisedController(cc)
with Logging {

implicit val endpointLogContext: EndpointLogContext =
EndpointLogContext(controllerName = "ListBsasController", endpointName = "listBsas")

def listBsas(nino: String, taxYear: Option[String], typeOfBusiness: Option[String], businessId: Option[String]): Action[AnyContent] =
authorisedAction(nino).async { implicit request =>
authorisedAction(nino, bsasConfig.secondaryAgentEndpointsAccessControlConfig.listBsas).async { implicit request =>
implicit val ctx: RequestContext = RequestContext.from(idGenerator, endpointLogContext)

val validator = validatorFactory.validator(nino, taxYear, typeOfBusiness, businessId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package v3.controllers

import config.BsasConfig
import play.api.mvc.{Action, AnyContent, ControllerComponents}
import shared.config.AppConfig
import shared.controllers.{AuthorisedController, EndpointLogContext, RequestContext, RequestHandler}
Expand All @@ -36,15 +37,16 @@ class RetrieveForeignPropertyBsasController @Inject() (val authService: Enrolmen
service: RetrieveForeignPropertyBsasService,
hateoasFactory: HateoasFactory,
cc: ControllerComponents,
val idGenerator: IdGenerator)(implicit ec: ExecutionContext, appConfig: AppConfig)
val idGenerator: IdGenerator,
val bsasConfig: BsasConfig)(implicit ec: ExecutionContext, appConfig: AppConfig)
extends AuthorisedController(cc)
with Logging {

implicit val endpointLogContext: EndpointLogContext =
EndpointLogContext(controllerName = "RetrieveForeignPropertyBsasController", endpointName = "retrieve")

def retrieve(nino: String, calculationId: String, taxYear: Option[String]): Action[AnyContent] =
authorisedAction(nino).async { implicit request =>
authorisedAction(nino, bsasConfig.secondaryAgentEndpointsAccessControlConfig.retrieveForeignPropertyBsas).async { implicit request =>
implicit val ctx: RequestContext = RequestContext.from(idGenerator, endpointLogContext)

val validator = validatorFactory.validator(nino, calculationId, taxYear)
Expand Down
6 changes: 4 additions & 2 deletions app/v3/controllers/RetrieveSelfEmploymentBsasController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package v3.controllers

import config.BsasConfig
import play.api.mvc.{Action, AnyContent, ControllerComponents}
import shared.config.AppConfig
import shared.controllers.{AuthorisedController, EndpointLogContext, RequestContext, RequestHandler}
Expand All @@ -36,15 +37,16 @@ class RetrieveSelfEmploymentBsasController @Inject() (val authService: Enrolment
service: RetrieveSelfEmploymentBsasService,
hateoasFactory: HateoasFactory,
cc: ControllerComponents,
val idGenerator: IdGenerator)(implicit ec: ExecutionContext, appConfig: AppConfig)
val idGenerator: IdGenerator,
val bsasConfig: BsasConfig)(implicit ec: ExecutionContext, appConfig: AppConfig)
extends AuthorisedController(cc)
with Logging {

implicit val endpointLogContext: EndpointLogContext =
EndpointLogContext(controllerName = "RetrieveSelfEmploymentBsasController", endpointName = "retrieve")

def handleRequest(nino: String, calculationId: String, taxYear: Option[String] = None): Action[AnyContent] =
authorisedAction(nino).async { implicit request =>
authorisedAction(nino, bsasConfig.secondaryAgentEndpointsAccessControlConfig.retrieveSelfEmploymentBsas).async { implicit request =>
implicit val ctx: RequestContext = RequestContext.from(idGenerator, endpointLogContext)

val validator = validatorFactory.validator(nino, calculationId, taxYear)
Expand Down
6 changes: 4 additions & 2 deletions app/v3/controllers/RetrieveUkPropertyBsasController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package v3.controllers

import config.BsasConfig
import play.api.mvc.{Action, AnyContent, ControllerComponents}
import shared.config.AppConfig
import shared.controllers.{AuthorisedController, EndpointLogContext, RequestContext, RequestHandler}
Expand All @@ -36,7 +37,8 @@ class RetrieveUkPropertyBsasController @Inject() (val authService: EnrolmentsAut
service: RetrieveUkPropertyBsasService,
hateoasFactory: HateoasFactory,
cc: ControllerComponents,
val idGenerator: IdGenerator)(implicit ec: ExecutionContext, appConfig: AppConfig)
val idGenerator: IdGenerator,
val bsasConfig: BsasConfig)(implicit ec: ExecutionContext, appConfig: AppConfig)
extends AuthorisedController(cc)
with Logging {

Expand All @@ -47,7 +49,7 @@ class RetrieveUkPropertyBsasController @Inject() (val authService: EnrolmentsAut
)

def retrieve(nino: String, calculationId: String, taxYear: Option[String]): Action[AnyContent] =
authorisedAction(nino).async { implicit request =>
authorisedAction(nino, bsasConfig.secondaryAgentEndpointsAccessControlConfig.retrieveUKPropertyBsas).async { implicit request =>
implicit val ctx: RequestContext = RequestContext.from(idGenerator, endpointLogContext)

val validator = validatorFactory.validator(nino, calculationId, taxYear)
Expand Down
6 changes: 4 additions & 2 deletions app/v3/controllers/SubmitForeignPropertyBsasController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package v3.controllers

import config.BsasConfig
import play.api.libs.json.JsValue
import play.api.mvc.{Action, ControllerComponents}
import shared.config.AppConfig
Expand All @@ -41,14 +42,15 @@ class SubmitForeignPropertyBsasController @Inject() (
hateoasFactory: HateoasFactory,
auditService: AuditService,
cc: ControllerComponents,
val idGenerator: IdGenerator)(implicit ec: ExecutionContext, appConfig: AppConfig)
val idGenerator: IdGenerator,
val bsasConfig: BsasConfig)(implicit ec: ExecutionContext, appConfig: AppConfig)
extends AuthorisedController(cc) {

implicit val endpointLogContext: EndpointLogContext =
EndpointLogContext(controllerName = "SubmitForeignPropertyBsasController", endpointName = "SubmitForeignPropertyBsas")

def handleRequest(nino: String, calculationId: String, taxYear: Option[String]): Action[JsValue] =
authorisedAction(nino).async(parse.json) { implicit request =>
authorisedAction(nino, bsasConfig.secondaryAgentEndpointsAccessControlConfig.submitForeignPropertyBsas).async(parse.json) { implicit request =>
implicit val ctx: RequestContext = RequestContext.from(idGenerator, endpointLogContext)

val validator = validatorFactory.validator(nino, calculationId, taxYear, request.body)
Expand Down
6 changes: 4 additions & 2 deletions app/v3/controllers/SubmitSelfEmploymentBsasController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package v3.controllers

import config.BsasConfig
import play.api.libs.json.JsValue
import play.api.mvc.{Action, ControllerComponents}
import shared.config.AppConfig
Expand All @@ -41,7 +42,8 @@ class SubmitSelfEmploymentBsasController @Inject() (
hateoasFactory: HateoasFactory,
auditService: AuditService,
cc: ControllerComponents,
val idGenerator: IdGenerator)(implicit ec: ExecutionContext, appConfig: AppConfig)
val idGenerator: IdGenerator,
val bsasConfig: BsasConfig)(implicit ec: ExecutionContext, appConfig: AppConfig)
extends AuthorisedController(cc) {

implicit val endpointLogContext: EndpointLogContext =
Expand All @@ -51,7 +53,7 @@ class SubmitSelfEmploymentBsasController @Inject() (
)

def submitSelfEmploymentBsas(nino: String, calculationId: String, taxYear: Option[String]): Action[JsValue] =
authorisedAction(nino).async(parse.json) { implicit request =>
authorisedAction(nino, bsasConfig.secondaryAgentEndpointsAccessControlConfig.submitSelfEmploymentBsas).async(parse.json) { implicit request =>
implicit val ctx: RequestContext = RequestContext.from(idGenerator, endpointLogContext)

val validator = validatorFactory.validator(nino, calculationId, taxYear, request.body)
Expand Down
6 changes: 4 additions & 2 deletions app/v3/controllers/SubmitUkPropertyBsasController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package v3.controllers

import config.BsasConfig
import play.api.libs.json.JsValue
import play.api.mvc.{Action, ControllerComponents}
import shared.config.AppConfig
Expand All @@ -39,14 +40,15 @@ class SubmitUkPropertyBsasController @Inject() (val authService: EnrolmentsAuthS
hateoasFactory: HateoasFactory,
auditService: AuditService,
cc: ControllerComponents,
val idGenerator: IdGenerator)(implicit ec: ExecutionContext, appConfig: AppConfig)
val idGenerator: IdGenerator,
val bsasConfig: BsasConfig)(implicit ec: ExecutionContext, appConfig: AppConfig)
extends AuthorisedController(cc) {

implicit val endpointLogContext: EndpointLogContext =
EndpointLogContext(controllerName = "SubmitUkPropertyBsasController", endpointName = "submitUkPropertyBsas")

def handleRequest(nino: String, calculationId: String, taxYear: Option[String]): Action[JsValue] =
authorisedAction(nino).async(parse.json) { implicit request =>
authorisedAction(nino, bsasConfig.secondaryAgentEndpointsAccessControlConfig.submitUKPropertyBsas).async(parse.json) { implicit request =>
implicit val ctx: RequestContext = RequestContext.from(idGenerator, endpointLogContext)

val validator = validatorFactory.validator(nino, calculationId, taxYear, request.body)
Expand Down
Loading