diff --git a/obp-api/src/main/scala/bootstrap/liftweb/CustomDBVendor.scala b/obp-api/src/main/scala/bootstrap/liftweb/CustomDBVendor.scala index 47db525326..9a0d58240a 100644 --- a/obp-api/src/main/scala/bootstrap/liftweb/CustomDBVendor.scala +++ b/obp-api/src/main/scala/bootstrap/liftweb/CustomDBVendor.scala @@ -49,6 +49,8 @@ class CustomDBVendor(driverName: String, if(maxLifetime.isDefined){ config.setMaxLifetime(maxLifetime.head) } + //Liftweb DB.scala will set all the new connections to false, so here we set default to false + config.setAutoCommit(false) (dbUser, dbPassword) match { case (Full(user), Full(pwd)) => diff --git a/obp-api/src/main/scala/code/api/util/APIUtil.scala b/obp-api/src/main/scala/code/api/util/APIUtil.scala index 864bacccbf..8f0f8a704e 100644 --- a/obp-api/src/main/scala/code/api/util/APIUtil.scala +++ b/obp-api/src/main/scala/code/api/util/APIUtil.scala @@ -995,6 +995,20 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{ } } + + /** only A-Z, a-z, 0-9, -, _, ., and max length <= 36 + * OBP APIUtil.generateUUID() length is 36 here.*/ + def checkObpId(value:String): String ={ + val valueLength = value.length + val regex = """^([A-Za-z0-9\-._]+)$""".r + value match { + case _ if value.isEmpty => SILENCE_IS_GOLDEN + case regex(e) if(valueLength <= 36) => SILENCE_IS_GOLDEN + case regex(e) if(valueLength > 36) => ErrorMessages.InvalidValueLength+" The maximum OBP id length is 36. " + case _ => ErrorMessages.InvalidValueCharacters + " only A-Z, a-z, 0-9, -, _, ., and max length <= 36 " + } + } + /** only A-Z, a-z, 0-9, -, _, ., @, space and max length <= 512 */ def checkUsernameString(value:String): String ={ val valueLength = value.length @@ -1819,6 +1833,21 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{ def checkAuth(cc: CallContext): Future[(Box[User], Option[CallContext])] = { if (isNeedCheckAuth) authenticatedAccessFun(cc) else anonymousAccessFun(cc) } + + def checkObpIds(obpKeyValuePairs: List[(String, String)], callContext: Option[CallContext]): Future[Option[CallContext]] = { + Future{ + val allInvalidValueParis = obpKeyValuePairs + .filter( + keyValuePair => + !checkObpId(keyValuePair._2).equals(SILENCE_IS_GOLDEN) + ) + if(allInvalidValueParis.nonEmpty){ + throw new RuntimeException(s"$InvalidJsonFormat Here are all invalid values: $allInvalidValueParis") + }else{ + callContext + } + } + } def checkRoles(bankId: Option[BankId], user: Box[User], cc: Option[CallContext]):Future[Box[Unit]] = { if (isNeedCheckRoles) { @@ -1915,6 +1944,11 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{ val originFn: CallContext => Box[JsonResponse] = obpEndpoint.apply(req) val pathParams = getPathParams(req.path.partPath) + + val allObpKeyValuePairs = if(req.request.method =="POST" &&req.json.isDefined) + getAllObpIdKeyValuePairs(req.json.getOrElse(JString(""))) + else Nil + val bankId = pathParams.get("BANK_ID").map(BankId(_)) val accountId = pathParams.get("ACCOUNT_ID").map(AccountId(_)) val viewId = pathParams.get("VIEW_ID").map(ViewId(_)) @@ -1941,6 +1975,8 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{ // if authentication check, do authorizedAccess, else do Rate Limit check for { (boxUser, callContext) <- checkAuth(cc) + + _ <- checkObpIds(allObpKeyValuePairs, callContext) // check bankId is valid (bank, callContext) <- checkBank(bankId, callContext) @@ -4813,4 +4849,16 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{ Empty, Empty) } + def getAllObpIdKeyValuePairs(json: JValue): List[(String, String)] = { + // all the OBP ids: + json + .filterField { + case JField(n, v) => + (n == "id" || n == "user_id" || n == "bank_id" || n == "account_id" || n == "customer_id" + || n == "branch_id" || n == "atm_id" || n == "transaction_id" || n == "transaction_request_id" + || n == "card_id"|| n == "view_id")&&v.isInstanceOf[JString]} + .map( + jField => (jField.name, jField.value.asInstanceOf[JString].s) + ) + } } diff --git a/obp-api/src/main/scala/code/api/util/ExampleValue.scala b/obp-api/src/main/scala/code/api/util/ExampleValue.scala index 026816edb0..8777fcab92 100644 --- a/obp-api/src/main/scala/code/api/util/ExampleValue.scala +++ b/obp-api/src/main/scala/code/api/util/ExampleValue.scala @@ -859,7 +859,7 @@ object ExampleValue { lazy val websiteExample = ConnectorField("www.openbankproject.com",NoDescriptionProvided) glossaryItems += makeGlossaryItem("website", websiteExample) - lazy val atmIdExample = ConnectorField("atme0352a-9a0f-4bfa-b30b-9003aa467f51","A string that MUST uniquely identify the ATM on this OBP instance.") + lazy val atmIdExample = ConnectorField("atme-9a0f-4bfa-b30b-9003aa467f51","A string that MUST uniquely identify the ATM on this OBP instance.") glossaryItems += makeGlossaryItem("atm_id", atmIdExample) lazy val atmAttributeIdExample = ConnectorField("xxaf2a-9a0f-4bfa-b30b-9003aa467f51","A string that MUST uniquely identify the ATM Attribute on this OBP instance.") diff --git a/obp-api/src/test/scala/code/setup/ServerSetup.scala b/obp-api/src/test/scala/code/setup/ServerSetup.scala index cbd8240542..ab0675994a 100644 --- a/obp-api/src/test/scala/code/setup/ServerSetup.scala +++ b/obp-api/src/test/scala/code/setup/ServerSetup.scala @@ -54,7 +54,9 @@ trait ServerSetup extends FeatureSpec with SendServerRequests setPropsValues("transactionRequests_supported_types" -> "SEPA,SANDBOX_TAN,FREE_FORM,COUNTERPARTY,ACCOUNT,ACCOUNT_OTP,SIMPLE,CARD") setPropsValues("CARD_OTP_INSTRUCTION_TRANSPORT" -> "DUMMY") setPropsValues("api_instance_id" -> "1_final") - + setPropsValues("starConnector_supported_types" -> "mapped,internal") + setPropsValues("connector" -> "star") + val server = TestServer def baseRequest = host(server.host, server.port) val secured = APIUtil.getPropsAsBoolValue("external.https", false) diff --git a/obp-api/src/test/scala/code/util/APIUtilTest.scala b/obp-api/src/test/scala/code/util/APIUtilTest.scala index efe9029af2..76b037409a 100644 --- a/obp-api/src/test/scala/code/util/APIUtilTest.scala +++ b/obp-api/src/test/scala/code/util/APIUtilTest.scala @@ -35,9 +35,11 @@ import code.api.util.APIUtil.{DateWithMsFormat, DefaultToDate, theEpochTime, _} import code.api.util.ErrorMessages._ import code.api.util._ import code.setup.PropsReset +import code.util.Helper.SILENCE_IS_GOLDEN import com.openbankproject.commons.model.UserAuthContextCommons import net.liftweb.common.{Box, Empty, Full} import net.liftweb.http.provider.HTTPParam +import net.liftweb.json.{JValue, parse} import org.scalatest.{FeatureSpec, GivenWhenThen, Matchers} class APIUtilTest extends FeatureSpec with Matchers with GivenWhenThen with PropsReset { @@ -748,6 +750,79 @@ class APIUtilTest extends FeatureSpec with Matchers with GivenWhenThen with Prop val actualValue = APIUtil.getBankIdAccountIdPairsFromUserAuthContexts(userAuthContexts) actualValue should be(expectedValue) } + + scenario(s"Test the getAllObpIdKeyValuePairs method") { + val json: JValue = parse( + """{ + | "account_id": "1", + | "id": "2", + | "customer_id": "3", + | "age": 1, + | "catchphrase": { + | "one": { + | "atm_id": "4", + | "value": { + | "one": { + | "one": { + | "bank_id": "5", + | "transaction_id": "6" + | }, + | "user_id": "7" + | }, + | "id":"8" + | } + | }, + | "card_id": "9" + | } + |}""".stripMargin) + + val actualValue = APIUtil.getAllObpIdKeyValuePairs(json) + val expectedValue= List( + ("account_id","1"), + ("id","2"), + ("customer_id","3"), + ("atm_id","4"), + ("bank_id","5"), + ("transaction_id","6"), + ("user_id","7"), + ("id","8"), + ("card_id","9") + ) + actualValue should be(expectedValue) + } + + + scenario(s"Test the checkObpId method") { + val id1 = "gh.29.uk" + val id2 = "1313_.121" + val id3 = APIUtil.generateUUID() + val id7 = "" //the empty string + + // error cases + val id4 = "+123313" //do not support + + val id5 = "@#$" //do not support @#$ + val id6 = APIUtil.generateUUID() +"1" //the max length is 36 + + val actualValue1 = APIUtil.checkObpId(id1) + val actualValue2 = APIUtil.checkObpId(id2) + val actualValue3 = APIUtil.checkObpId(id3) + + val actualValue4 = APIUtil.checkObpId(id4) + val actualValue5 = APIUtil.checkObpId(id5) + val actualValue6 = APIUtil.checkObpId(id6) + val actualValue7 = APIUtil.checkObpId(id7) + + + SILENCE_IS_GOLDEN should be(actualValue1) + SILENCE_IS_GOLDEN should be(actualValue2) + SILENCE_IS_GOLDEN should be(actualValue3) + SILENCE_IS_GOLDEN should be(actualValue7) + + actualValue4 contains (InvalidValueCharacters) shouldBe (true) + actualValue5 contains (InvalidValueCharacters) shouldBe (true) + actualValue6 contains (InvalidValueLength) shouldBe (true) + + } }