diff --git a/README.Akka.md b/README.Akka.md index baf775131..28cdba853 100644 --- a/README.Akka.md +++ b/README.Akka.md @@ -28,6 +28,8 @@ remotedata.enable=false remotedata.hostname=10.0.0.19 # Arbitrary port of your choosing remotedata.port=5448 +# Arbitrary value used in order to assure us that remote and local sides are paired well +remotedata.secret=secret # Optionally configure postgres, otherwise file-based H2 will be used remotedata.db.driver=org.postgresql.Driver diff --git a/src/main/resources/props/sample.props.template b/src/main/resources/props/sample.props.template index 2671fabaa..817db4099 100644 --- a/src/main/resources/props/sample.props.template +++ b/src/main/resources/props/sample.props.template @@ -92,6 +92,8 @@ db.url=jdbc:postgresql://localhost:5432/dbname?user=dbusername&password=thepassw #remotedata.enable=true #remotedata.hostname=127.0.0.1 #remotedata.port=2662 +# Arbitrary value used in order to assure us that remote and local sides are paired well +#remotedata.secret=secret ## Set separate database for data split ## If remotedata is disabled, bd has to be accessible from local machine diff --git a/src/main/resources/props/test.default.props.template b/src/main/resources/props/test.default.props.template index c24e7623a..a9cf16e6b 100644 --- a/src/main/resources/props/test.default.props.template +++ b/src/main/resources/props/test.default.props.template @@ -45,6 +45,14 @@ hostname=http://localhost:8016 #if you want to change the port when running via the command line, use "mvn -Djetty.port=8089 jetty:run" instead tests.port=8016 +## Enable remote Akka actor for data split +## If set to true, must set hostname and port +## of remote machine +#remotedata.enable=false +#remotedata.hostname=127.0.0.1 +#remotedata.port=2662 +# Arbitrary value used in order to assure us that remote and local sides are paired well +remotedata.secret=secret End of minimum settings #################################### diff --git a/src/main/scala/bootstrap/liftweb/Boot.scala b/src/main/scala/bootstrap/liftweb/Boot.scala index 776fe0f50..7d112ade6 100644 --- a/src/main/scala/bootstrap/liftweb/Boot.scala +++ b/src/main/scala/bootstrap/liftweb/Boot.scala @@ -40,6 +40,7 @@ import code.api.Constant._ import code.api.ResourceDocs1_4_0.ResourceDocs import code.api._ import code.api.sandbox.SandboxApiCalls +import code.api.util.{APIUtil, ErrorMessages} import code.atms.MappedAtm import code.branches.MappedBranch import code.cards.{MappedPhysicalCard, PinReset} @@ -374,6 +375,13 @@ class Boot extends Loggable{ TransactionStatusScheduler.start(delay) } + APIUtil.akkaSanityCheck() match { + case Full(c) if c == true => logger.info(s"remotedata.secret matched = $c") + case Full(c) if c == false => throw new Exception(ErrorMessages.RemoteDataSecretMatchError) + case Empty => throw new Exception(ErrorMessages.RemoteDataSecretObtainError) + case _ => throw new Exception(s"Unexpected error occurs during Akka sanity check!") + } + } def schemifyAll() = { diff --git a/src/main/scala/code/api/ResourceDocs1_4_0/ResourceDocsAPIMethods.scala b/src/main/scala/code/api/ResourceDocs1_4_0/ResourceDocsAPIMethods.scala index 6cbb01cf7..1fa8f45a2 100644 --- a/src/main/scala/code/api/ResourceDocs1_4_0/ResourceDocsAPIMethods.scala +++ b/src/main/scala/code/api/ResourceDocs1_4_0/ResourceDocsAPIMethods.scala @@ -1,6 +1,7 @@ package code.api.ResourceDocs1_4_0 import code.api.util.APIUtil +import code.api.v1_2_1.Akka import code.api.v1_4_0.{APIMethods140, JSONFactory1_4_0, OBPAPI1_4_0} import code.api.v2_2_0.{APIMethods220, OBPAPI2_2_0} import code.bankconnectors.{KafkaJSONFactory_vMar2017, KafkaMappedConnector_vMar2017} @@ -302,7 +303,7 @@ def filterResourceDocs(allResources: List[ResourceDoc]) : List[ResourceDoc] = { user => val apiDetails: JValue = { val hostedBy = new HostedBy("Dummy Org", "contact@example.com", "12345") - val apiInfoJSON = new APIInfoJSON(apiVersion, apiVersionStatus, gitCommit, "dummy-connector", hostedBy) + val apiInfoJSON = new APIInfoJSON(apiVersion, apiVersionStatus, gitCommit, "dummy-connector", hostedBy, Akka(APIUtil.akkaSanityCheck())) Extraction.decompose(apiInfoJSON) } diff --git a/src/main/scala/code/api/util/APIUtil.scala b/src/main/scala/code/api/util/APIUtil.scala index 56afae497..d66054550 100644 --- a/src/main/scala/code/api/util/APIUtil.scala +++ b/src/main/scala/code/api/util/APIUtil.scala @@ -44,6 +44,7 @@ import code.customer.Customer import code.entitlement.Entitlement import code.metrics.{APIMetrics, ConnMetrics} import code.model._ +import code.sanitycheck.SanityCheck import dispatch.url import net.liftweb.common.{Empty, _} import net.liftweb.http.js.JE.JsRaw @@ -108,6 +109,9 @@ object ErrorMessages { val InsufficientAuthorisationToCreateBranch = "OBP-20019: Insufficient authorisation to Create Branch offered by the bank. The Request could not be created because you don't have access to CanCreateBranch." val InsufficientAuthorisationToCreateBank = "OBP-20020: Insufficient authorisation to Create Bank. The Request could not be created because you don't have access to CanCreateBank." + + val RemoteDataSecretMatchError = "OBP-20021: Remote data secret cannot be matched!" + val RemoteDataSecretObtainError = "OBP-20021: Remote data secret cannot be obtained!" // Resource related messages val BankNotFound = "OBP-30001: Bank not found. Please specify a valid value for BANK_ID." @@ -926,4 +930,9 @@ Returns a string showed to the developer result } + def akkaSanityCheck (): Box[Boolean] = { + val remoteDataSecret = Props.get("remotedata.secret").openOrThrowException("Cannot obtain property remotedata.secret") + SanityCheck.sanityCheck.vend.remoteAkkaSanityCheck(remoteDataSecret) + } + } diff --git a/src/main/scala/code/api/v1_2_1/APIMethods121.scala b/src/main/scala/code/api/v1_2_1/APIMethods121.scala index 7fccae03e..495a72a27 100644 --- a/src/main/scala/code/api/v1_2_1/APIMethods121.scala +++ b/src/main/scala/code/api/v1_2_1/APIMethods121.scala @@ -1,25 +1,21 @@ package code.api.v1_2_1 +import java.net.URL + import code.api.util.APIUtil +import code.api.util.APIUtil._ +import code.bankconnectors.{OBPFromDate, OBPOffset, OBPToDate, _} +import code.metadata.counterparties.Counterparties +import code.model.{CreateViewJSON, UpdateViewJSON, _} +import code.sanitycheck.SanityCheck +import net.liftweb.common.{Full, _} +import net.liftweb.http.rest.RestHelper import net.liftweb.http.{JsonResponse, Req} import net.liftweb.json.Extraction -import net.liftweb.common._ -import code.model._ import net.liftweb.json.Extraction._ import net.liftweb.json.JsonAST.JValue -import APIUtil._ import net.liftweb.util.Helpers._ -import net.liftweb.http.rest.RestHelper -import java.net.URL -import net.liftweb.util.{True, Props} -import code.bankconnectors._ -import code.bankconnectors.OBPOffset -import code.bankconnectors.OBPFromDate -import code.bankconnectors.OBPToDate -import code.metadata.counterparties.Counterparties -import code.model.CreateViewJSON -import net.liftweb.common.Full -import code.model.UpdateViewJSON +import net.liftweb.util.Props import scala.collection.immutable.Nil import scala.collection.mutable.ArrayBuffer @@ -82,7 +78,7 @@ trait APIMethods121 { val connector = Props.get("connector").openOrThrowException("no connector set") val hostedBy = new HostedBy(organisation, email, phone) - val apiInfoJSON = new APIInfoJSON(apiVersion, apiVersionStatus, gitCommit, connector, hostedBy) + val apiInfoJSON = new APIInfoJSON(apiVersion, apiVersionStatus, gitCommit, connector, hostedBy, Akka(APIUtil.akkaSanityCheck())) Extraction.decompose(apiInfoJSON) } apiDetails diff --git a/src/main/scala/code/api/v1_2_1/JSONFactory1.2.1.scala b/src/main/scala/code/api/v1_2_1/JSONFactory1.2.1.scala index c316c40cd..a181316ee 100644 --- a/src/main/scala/code/api/v1_2_1/JSONFactory1.2.1.scala +++ b/src/main/scala/code/api/v1_2_1/JSONFactory1.2.1.scala @@ -40,13 +40,15 @@ case class APIInfoJSON( version_status: String, git_commit : String, connector : String, - hosted_by : HostedBy + hosted_by : HostedBy, + akka: Akka ) case class HostedBy( organisation : String, email : String, phone : String ) +case class Akka(remote_data_secret_matched: Option[Boolean]) case class ErrorMessage( error : String ) diff --git a/src/main/scala/code/api/v1_4_0/APIMethods140.scala b/src/main/scala/code/api/v1_4_0/APIMethods140.scala index b24b54631..dd7246833 100644 --- a/src/main/scala/code/api/v1_4_0/APIMethods140.scala +++ b/src/main/scala/code/api/v1_4_0/APIMethods140.scala @@ -1,5 +1,6 @@ package code.api.v1_4_0 +import code.api.util.APIUtil import code.api.util.APIUtil.isValidCurrencyISOCode import code.api.util.ApiRole.{CanCreateCustomer, CanCreateUserCustomerLink} import code.api.v1_4_0.JSONFactory1_4_0._ @@ -10,13 +11,13 @@ import net.liftweb.common.{Box, Full, Loggable} import net.liftweb.http.js.JE.JsRaw import net.liftweb.http.{JsonResponse, Req} import net.liftweb.http.rest.RestHelper -import net.liftweb.json.{Extraction} +import net.liftweb.json.Extraction import net.liftweb.json.JsonAST.{JField, JObject, JValue} import net.liftweb.util.Helpers.tryo import net.liftweb.json.JsonDSL._ import net.liftweb.util.Props import net.liftweb.json.JsonAST.JValue -import code.api.v1_2_1.AmountOfMoneyJSON +import code.api.v1_2_1.{Akka, AmountOfMoneyJSON} import code.api.v2_0_0.CreateCustomerJson import scala.collection.immutable.Nil @@ -657,7 +658,7 @@ trait APIMethods140 extends Loggable with APIMethods130 with APIMethods121{ user => val apiDetails: JValue = { val hostedBy = new HostedBy("Dummy Org", "contact@example.com", "12345") - val apiInfoJSON = new APIInfoJSON(apiVersion, apiVersionStatus, gitCommit, "DUMMY", hostedBy) + val apiInfoJSON = new APIInfoJSON(apiVersion, apiVersionStatus, gitCommit, "DUMMY", hostedBy, Akka(APIUtil.akkaSanityCheck())) Extraction.decompose(apiInfoJSON) } diff --git a/src/main/scala/code/remotedata/RemotedataActors.scala b/src/main/scala/code/remotedata/RemotedataActors.scala index 7aaf689e8..3a23ca8ba 100644 --- a/src/main/scala/code/remotedata/RemotedataActors.scala +++ b/src/main/scala/code/remotedata/RemotedataActors.scala @@ -102,7 +102,8 @@ object RemotedataActors extends Loggable { ActorProps[RemotedataMetricsActor] -> RemotedataMetrics.actorName, ActorProps[RemotedataTokensActor] -> RemotedataTokens.actorName, ActorProps[RemotedataNoncesActor] -> RemotedataNonces.actorName, - ActorProps[RemotedataConnectorMetricsActor] -> RemotedataConnectorMetrics.actorName + ActorProps[RemotedataConnectorMetricsActor] -> RemotedataConnectorMetrics.actorName, + ActorProps[RemotedataSanityCheckActor] -> RemotedataSanityCheck.actorName ) actorsRemotedata.foreach { a => logger.info(actorSystem.actorOf(a._1, name = a._2)) } diff --git a/src/main/scala/code/remotedata/RemotedataSanityCheck.scala b/src/main/scala/code/remotedata/RemotedataSanityCheck.scala new file mode 100644 index 000000000..e697fa84c --- /dev/null +++ b/src/main/scala/code/remotedata/RemotedataSanityCheck.scala @@ -0,0 +1,15 @@ +package code.remotedata + +import akka.pattern.ask +import code.sanitycheck.{RemotedataSanityCheckCaseClasses, SanityChecks} +import net.liftweb.common.Box + + +object RemotedataSanityCheck extends ActorInit with SanityChecks { + + val cc = RemotedataSanityCheckCaseClasses + + def remoteAkkaSanityCheck(remoteDataSecret: String): Box[Boolean] = + extractFutureToBox(actor ? cc.remoteAkkaSanityCheck(remoteDataSecret)) + +} diff --git a/src/main/scala/code/remotedata/RemotedataSanityCheckActor.scala b/src/main/scala/code/remotedata/RemotedataSanityCheckActor.scala new file mode 100644 index 000000000..d74de9674 --- /dev/null +++ b/src/main/scala/code/remotedata/RemotedataSanityCheckActor.scala @@ -0,0 +1,26 @@ +package code.remotedata + +import akka.actor.Actor +import akka.event.Logging +import code.sanitycheck.{RemotedataSanityCheckCaseClasses, SanityChecksImpl} + + +class RemotedataSanityCheckActor extends Actor with ActorHelper { + + val logger = Logging(context.system, this) + + val mapper = SanityChecksImpl + val cc = RemotedataSanityCheckCaseClasses + + def receive = { + + case cc.remoteAkkaSanityCheck(remoteDataSecret: String) => + logger.debug("remoteAkkaSanityCheck()") + sender ! extractResult(mapper.remoteAkkaSanityCheck(remoteDataSecret)) + + case message => logger.warning("[AKKA ACTOR ERROR - REQUEST NOT RECOGNIZED] " + message) + + } + +} + diff --git a/src/main/scala/code/sanitycheck/SanityCheck.scala b/src/main/scala/code/sanitycheck/SanityCheck.scala new file mode 100644 index 000000000..e18452994 --- /dev/null +++ b/src/main/scala/code/sanitycheck/SanityCheck.scala @@ -0,0 +1,32 @@ +package code.sanitycheck + +import code.remotedata.RemotedataSanityCheck +import net.liftweb.common.{Box, Full, Empty} +import net.liftweb.util.{Props, SimpleInjector} + +object SanityCheck extends SimpleInjector { + + val sanityCheck = new Inject(buildOne _) {} + + def buildOne: SanityChecks = RemotedataSanityCheck + +} + +trait SanityChecks { + def remoteAkkaSanityCheck(remoteDataSecret: String): Box[Boolean] +} + +class RemotedataSanityCheckCaseClasses { + case class remoteAkkaSanityCheck(remoteDataSecret: String) +} + +object RemotedataSanityCheckCaseClasses extends RemotedataSanityCheckCaseClasses + +object SanityChecksImpl extends SanityChecks { + override def remoteAkkaSanityCheck(remoteDataSecret: String): Box[Boolean] = { + Props.get("remotedata.secret") match { + case Full(x) => Full(remoteDataSecret == x) + case _ => Empty + } + } +}