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

CIR-1651: Some cleaning up of the controller #89

Merged
merged 3 commits into from
Jul 30, 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
29 changes: 14 additions & 15 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@

logs
project/project
**/.bloop/
**/.metals/
**/.vscode/
**/target/
lib_managed
tmp
*.iws
.history
dist
/.idea
/*.iml
/*.ipr
/out
/.idea_modules
/.bsp
/.classpath
/.idea
/.idea_modules
/.project
/RUNNING_PID
/.settings
*.iws
/.bsp
/null
**/.bloop/
**/.metals/
**/.vscode/
/out
/RUNNING_PID
dist
lib_managed
logs
project/project
tmp
2 changes: 2 additions & 0 deletions .scalafmt.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
version = 3.7.15
runner.dialect = scala213
3 changes: 2 additions & 1 deletion app/Module.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
import com.google.inject.AbstractModule
import play.api.{Configuration, Environment}

class Module(environment: Environment, configuration: Configuration) extends AbstractModule {
class Module(environment: Environment, configuration: Configuration)
extends AbstractModule {

override def configure(): Unit = {}
}
67 changes: 45 additions & 22 deletions app/access/AccessChecker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@

package access

import access.AccessChecker.{accessControlAllowListAbsoluteKey, accessControlEnabledAbsoluteKey, accessRequestFormUrlAbsoluteKey}
import access.AccessChecker.{
accessControlAllowListAbsoluteKey,
accessControlEnabledAbsoluteKey,
accessRequestFormUrlAbsoluteKey
}
import config.AppConfig
import org.slf4j.LoggerFactory
import play.api.http.HeaderNames
Expand All @@ -34,19 +38,31 @@ trait AccessChecker {

private val logger = LoggerFactory.getLogger(this.getClass)

private val accessRequestFormUrl: String = configHelper.mustGetConfigString(accessRequestFormUrlAbsoluteKey)
private val accessRequestFormUrl: String =
configHelper.mustGetConfigString(accessRequestFormUrlAbsoluteKey)

private val checkAllowList: Boolean = configHelper.mustGetConfigString(accessControlEnabledAbsoluteKey).toBoolean
private val allowedClients: Set[String] = configHelper.config.getOptional[Seq[String]](accessControlAllowListAbsoluteKey).getOrElse(
if (checkAllowList) throw new RuntimeException(s"Could not find config $accessControlAllowListAbsoluteKey") else Seq()).toSet
private val checkAllowList: Boolean =
configHelper.mustGetConfigString(accessControlEnabledAbsoluteKey).toBoolean
private val allowedClients: Set[String] = configHelper.config
.getOptional[Seq[String]](accessControlAllowListAbsoluteKey)
.getOrElse(
if (checkAllowList)
throw new RuntimeException(
s"Could not find config $accessControlAllowListAbsoluteKey"
)
else Seq()
)
.toSet

private def areClientsAllowed(clients: Seq[String]): Boolean =
clients.forall(allowedClients.contains)

private def forbiddenResponse(clients: Seq[String]): String =
s"""{
|"code": ${FORBIDDEN},
|"description": "One or more user agents in '${clients.mkString(",")}' are not authorized to use this service. Please complete '${accessRequestFormUrl}' to request access."
|"description": "One or more user agents in '${clients.mkString(
","
)}' are not authorized to use this service. Please complete '${accessRequestFormUrl}' to request access."
|}""".stripMargin

private def getClientsFromRequest[T](req: Request[T]): Seq[String] = {
Expand All @@ -59,30 +75,37 @@ trait AccessChecker {
}
}

def accessCheckedAction[A](bodyParser: BodyParser[A])(block: Request[A] => Future[Result]): Action[A] = {
Action.async(bodyParser) {
request =>
val callingClients = getClientsFromRequest(request)
if (!areClientsAllowed(callingClients)) {
if (checkAllowList) {
Future.successful(Forbidden(Json.parse(forbiddenResponse(callingClients))))
} else {
logger.warn(s"One or more user agents in '${callingClients.mkString(",")}' are not authorized to use this service")
block(request)
}
}
else {
def accessCheckedAction[A](
bodyParser: BodyParser[A]
)(block: Request[A] => Future[Result]): Action[A] = {
Action.async(bodyParser) { request =>
val callingClients = getClientsFromRequest(request)
if (!areClientsAllowed(callingClients)) {
if (checkAllowList) {
Future.successful(
Forbidden(Json.parse(forbiddenResponse(callingClients)))
)
} else {
logger.warn(
s"One or more user agents in '${callingClients.mkString(",")}' are not authorized to use this service"
)
block(request)
}
} else {
block(request)
}
}
}
}

object AccessChecker {
val accessRequestFormUrlKey = "access-control.request.formUrl"
val accessRequestFormUrlAbsoluteKey = s"microservice.services.$accessRequestFormUrlKey"
val accessRequestFormUrlAbsoluteKey =
s"microservice.services.$accessRequestFormUrlKey"
val accessControlEnabledKey = "access-control.enabled"
val accessControlEnabledAbsoluteKey = s"microservice.services.$accessControlEnabledKey"
val accessControlEnabledAbsoluteKey =
s"microservice.services.$accessControlEnabledKey"
val accessControlAllowListKey = "access-control.allow-list"
val accessControlAllowListAbsoluteKey = s"microservice.services.$accessControlAllowListKey"
val accessControlAllowListAbsoluteKey =
s"microservice.services.$accessControlAllowListKey"
}
14 changes: 11 additions & 3 deletions app/apiplatform/DocumentationController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,19 @@ import uk.gov.hmrc.play.bootstrap.backend.controller.BackendController
import javax.inject.Inject
import scala.concurrent.Future

class DocumentationController @Inject()(assets: Assets, cc: ControllerComponents, configHelper: AppConfig) extends BackendController(cc) {
private val apiStatus = configHelper.mustGetConfigString("api-platform.status")
class DocumentationController @Inject() (
assets: Assets,
cc: ControllerComponents,
configHelper: AppConfig
) extends BackendController(cc) {
private val apiStatus =
configHelper.mustGetConfigString("api-platform.status")

def definition(): Action[AnyContent] = Action.async {
Future.successful(Ok(txt.definition(apiStatus)).as(ContentTypes.withCharset(MimeTypes.JSON)(Codec.utf_8)))
Future.successful(
Ok(txt.definition(apiStatus))
.as(ContentTypes.withCharset(MimeTypes.JSON)(Codec.utf_8))
)
}

def raml(version: String, file: String): Action[AnyContent] = {
Expand Down
115 changes: 115 additions & 0 deletions app/audit/Auditor.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
* Copyright 2024 HM Revenue & Customs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package audit

import model.{
AddressSearchAuditEvent,
AddressSearchAuditEventMatchedAddress,
AddressSearchAuditEventRequestDetails,
NonUKAddressSearchAuditEvent,
NonUKAddressSearchAuditEventMatchedAddress,
NonUKAddressSearchAuditEventRequestDetails
}
import model.address.{AddressRecord, NonUKAddress, Postcode}
import model.request.UserAgent
import uk.gov.hmrc.http.HeaderCarrier
import uk.gov.hmrc.play.audit.http.connector.AuditConnector

import javax.inject.Inject
import scala.concurrent.ExecutionContext

class Auditor @Inject() (auditConnector: AuditConnector)(implicit
ec: ExecutionContext
) {
def auditAddressSearch[A](
userAgent: Option[UserAgent],
addressRecords: List[AddressRecord],
postcode: Option[Postcode] = None,
posttown: Option[String] = None,
uprn: Option[String] = None,
filter: Option[String] = None
)(implicit hc: HeaderCarrier): Unit = {

if (addressRecords.nonEmpty) {
val auditEventRequestDetails = AddressSearchAuditEventRequestDetails(
postcode.map(_.toString),
posttown,
uprn,
filter
)
val addressSearchAuditEventMatchedAddresses = addressRecords.map { ma =>
AddressSearchAuditEventMatchedAddress(
ma.uprn.map(_.toString).getOrElse(""),
ma.parentUprn,
ma.usrn,
ma.organisation,
ma.address.lines,
ma.address.town,
ma.localCustodian,
ma.location,
ma.administrativeArea,
ma.poBox,
ma.address.postcode,
ma.address.subdivision,
ma.address.country
)
}

auditConnector.sendExplicitAudit(
"AddressSearch",
AddressSearchAuditEvent(
userAgent.map(_.unwrap),
auditEventRequestDetails,
addressRecords.length,
addressSearchAuditEventMatchedAddresses
)
)
}
}

def auditNonUKAddressSearch[A](
userAgent: Option[UserAgent],
nonUKAddresses: List[NonUKAddress],
country: String,
filter: Option[String] = None
)(implicit hc: HeaderCarrier): Unit = {

if (nonUKAddresses.nonEmpty) {
auditConnector.sendExplicitAudit(
"NonUKAddressSearch",
NonUKAddressSearchAuditEvent(
userAgent.map(_.unwrap),
NonUKAddressSearchAuditEventRequestDetails(filter),
nonUKAddresses.length,
nonUKAddresses.map { ma =>
NonUKAddressSearchAuditEventMatchedAddress(
ma.id,
ma.number,
ma.street,
ma.unit,
ma.city,
ma.district,
ma.region,
ma.postcode,
country
)
}
)
)
}
}
}
20 changes: 14 additions & 6 deletions app/config/AppConfig.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ import java.util.Base64
import javax.inject.Singleton

@Singleton
class AppConfig @Inject()(val config: Configuration, servicesConfig: ServicesConfig) {
class AppConfig @Inject() (
val config: Configuration,
servicesConfig: ServicesConfig
) {
val appName = config.get[String]("appName")

val addressSearchApiBaseUrl = servicesConfig.baseUrl("address-search-api")
Expand All @@ -40,14 +43,19 @@ class AppConfig @Inject()(val config: Configuration, servicesConfig: ServicesCon
private def createAuth =
AppConfig.createAuth(
config.get[String]("appName"),
servicesConfig.getConfString("addressSearchApiAuthToken", "invalid-token"))
servicesConfig.getConfString("addressSearchApiAuthToken", "invalid-token")
)

def isCipPaasDbEnabled: Boolean =
config.getOptional[String]("cip-address-lookup-rds.enabled").getOrElse("false").toBoolean
config
.getOptional[String]("cip-address-lookup-rds.enabled")
.getOrElse("false")
.toBoolean
}

object AppConfig {
def createAuth(appName: String, authToken: String): String = Base64.getEncoder.encodeToString(
s"$appName:$authToken".getBytes(StandardCharsets.UTF_8)
)
def createAuth(appName: String, authToken: String): String =
Base64.getEncoder.encodeToString(
s"$appName:$authToken".getBytes(StandardCharsets.UTF_8)
)
}
Loading