diff --git a/README.md b/README.md index 1b1dad292..c9317d84f 100644 --- a/README.md +++ b/README.md @@ -497,8 +497,8 @@ In order to make it work edit your props file in next way: ``` use_consumer_limits=false, In case isn't defined default value is "false" -redis_address=YOUR_REDIS_URL_ADDRESS, In case isn't defined default value is 127.0.0.1 -redis_port=YOUR_REDIS_PORT, In case isn't defined default value is 6379 +cache.redis.url=YOUR_REDIS_URL_ADDRESS, In case isn't defined default value is 127.0.0.1 +cache.redis.port=YOUR_REDIS_PORT, In case isn't defined default value is 6379 ``` The next types are supported: diff --git a/obp-api/pom.xml b/obp-api/pom.xml index 4cae0ddf6..1227da21a 100644 --- a/obp-api/pom.xml +++ b/obp-api/pom.xml @@ -503,7 +503,13 @@ jakarta.activation 1.2.2 - + + + + com.nulab-inc + zxcvbn + 1.9.0 + diff --git a/obp-api/src/main/resources/props/sample.props.template b/obp-api/src/main/resources/props/sample.props.template index f2baea7a1..64c7733d3 100644 --- a/obp-api/src/main/resources/props/sample.props.template +++ b/obp-api/src/main/resources/props/sample.props.template @@ -856,8 +856,7 @@ featured_apis=elasticSearchWarehouseV300 # use_consumer_limits=false # In case isn't defined default value is 60 # user_consumer_limit_anonymous_access=100 -# redis_address=127.0.0.1 -# redis_port=6379 +# For the Rate Limiting feature we use Redis cache instance # In case isn't defined default value is root # rate_limiting.exclude_endpoints=root ## Default rate limiting for a new consumer diff --git a/obp-api/src/main/scala/code/api/berlin/group/v1_3/JSONFactory_BERLIN_GROUP_1_3.scala b/obp-api/src/main/scala/code/api/berlin/group/v1_3/JSONFactory_BERLIN_GROUP_1_3.scala index 13aa39560..f7857a00d 100644 --- a/obp-api/src/main/scala/code/api/berlin/group/v1_3/JSONFactory_BERLIN_GROUP_1_3.scala +++ b/obp-api/src/main/scala/code/api/berlin/group/v1_3/JSONFactory_BERLIN_GROUP_1_3.scala @@ -473,27 +473,30 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats with MdcLoggable{ val bookingDate = transaction.startDate.orNull val valueDate = if(transaction.finishDate.isDefined) Some(BgSpecValidation.formatToISODate(transaction.finishDate.orNull)) else None - val creditorName = transaction.otherBankAccount.map(_.label.display).getOrElse("") - val creditorAccountIban = stringOrNone(transaction.otherBankAccount.map(_.iban.getOrElse("")).getOrElse("")) - - val debtorName = stringOrNone(transaction.bankAccount.map(_.label.getOrElse("")).getOrElse("")) - val debtorIban = transaction.bankAccount.map(_.accountRoutingAddress.getOrElse("")).getOrElse("") - val debtorAccountIdIban = stringOrNone(debtorIban) + val out: Boolean = transaction.amount.get.toString().startsWith("-") + val in: Boolean = !out + + val isIban = transaction.bankAccount.flatMap(_.accountRoutingScheme.map(_.toUpperCase == "IBAN")).getOrElse(false) + // Creditor + val creditorName = if(in) transaction.otherBankAccount.map(_.label.display) else None + val creditorAccountIban = if(in) { + val creditorIban = if(isIban) transaction.otherBankAccount.map(_.iban.getOrElse("")) else Some("") + Some(BgTransactionAccountJson(iban = creditorIban)) + } else None + + // Debtor + val debtorName = if(out) transaction.bankAccount.map(_.label.getOrElse("")) else None + val debtorAccountIban = if(out) { + val debtorIban = if(isIban) transaction.bankAccount.map(_.accountRoutingAddress.getOrElse("")) else Some("") + Some(BgTransactionAccountJson(iban = debtorIban)) + } else None TransactionJsonV13( transactionId = transaction.id.value, - creditorName = stringOrNone(creditorName), - creditorAccount = - if(creditorAccountIban.isEmpty) - None - else - Some(BgTransactionAccountJson(iban=creditorAccountIban)), + creditorName = creditorName, + creditorAccount = creditorAccountIban, debtorName = debtorName, - debtorAccount = - if(debtorAccountIdIban.isEmpty) - None - else - Some(BgTransactionAccountJson(iban = debtorAccountIdIban)), + debtorAccount = debtorAccountIban, transactionAmount = AmountOfMoneyV13( transaction.currency.getOrElse(""), if(bgRemoveSignOfAmounts) 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 376d3ea68..db5049469 100644 --- a/obp-api/src/main/scala/code/api/util/APIUtil.scala +++ b/obp-api/src/main/scala/code/api/util/APIUtil.scala @@ -3974,7 +3974,8 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{ tpp <- BerlinGroupSigning.getTppByCertificate(certificate, cc) } yield { if (tpp.nonEmpty) { - val hasRole = tpp.exists(_.services.contains(serviceProvider)) + val berlinGroupRole = PemCertificateRole.toBerlinGroup(serviceProvider) + val hasRole = tpp.exists(_.services.contains(berlinGroupRole)) if (hasRole) { Full(true) } else { diff --git a/obp-api/src/main/scala/code/api/util/PasswordUtil.scala b/obp-api/src/main/scala/code/api/util/PasswordUtil.scala new file mode 100644 index 000000000..5353a0fd8 --- /dev/null +++ b/obp-api/src/main/scala/code/api/util/PasswordUtil.scala @@ -0,0 +1,21 @@ +package code.api.util + +import com.nulabinc.zxcvbn.Zxcvbn +import com.nulabinc.zxcvbn.Strength + +object PasswordUtil { + + private val zxcvbn = new Zxcvbn() + + /** Check password strength score: 0 (very weak) to 4 (very strong) */ + def getStrength(password: String): Strength = { + zxcvbn.measure(password) + } + + /** Recommend minimum score of 3 (strong) */ + def isAcceptable(password: String, minScore: Int = 3): Boolean = { + getStrength(password).getScore >= minScore + } + +} + diff --git a/obp-api/src/test/scala/code/api/berlin/group/v1_3/AccountInformationServiceAISApiTest.scala b/obp-api/src/test/scala/code/api/berlin/group/v1_3/AccountInformationServiceAISApiTest.scala index 685e0b907..b97039db9 100644 --- a/obp-api/src/test/scala/code/api/berlin/group/v1_3/AccountInformationServiceAISApiTest.scala +++ b/obp-api/src/test/scala/code/api/berlin/group/v1_3/AccountInformationServiceAISApiTest.scala @@ -185,14 +185,32 @@ class AccountInformationServiceAISApiTest extends BerlinGroupServerSetupV1_3 wit user1, PostViewJsonV400(view_id = Constant.SYSTEM_READ_TRANSACTIONS_BERLIN_GROUP_VIEW_ID, is_system = true) ) - val requestGet = (V1_3_BG / "accounts" /testAccountId1.value/ "transactions").GET <@ (user1) + val requestGet = (V1_3_BG / "accounts" /testAccountId1.value/ "transactions").GET <@ (user1) <0 should be (true) - response.body.extract[TransactionsJsonV13].transactions.pending.head.length >0 should be (true) + response.body.extract[TransactionsJsonV13].transactions.pending.head.nonEmpty should be (true) + response.body.extract[TransactionsJsonV13].transactions.booked.nonEmpty should be (true) + + + val requestGet2 = (V1_3_BG / "accounts" / testAccountId1.value / "transactions").GET <@ (user1) < 0 should be (true) + response.body.extract[TransactionsJsonV13].transactions.pending.head.nonEmpty should be (true) // response.body.extract[TransactionsJsonV13].transactions.pending.length > 0 should be (true) val transactionId = response.body.extract[TransactionsJsonV13].transactions.pending.head.head.transactionId diff --git a/obp-api/src/test/scala/code/api/berlin/group/v1_3/JSONFactory_BERLIN_GROUP_1_3Test.scala b/obp-api/src/test/scala/code/api/berlin/group/v1_3/JSONFactory_BERLIN_GROUP_1_3Test.scala index c08b84132..8e0cda195 100644 --- a/obp-api/src/test/scala/code/api/berlin/group/v1_3/JSONFactory_BERLIN_GROUP_1_3Test.scala +++ b/obp-api/src/test/scala/code/api/berlin/group/v1_3/JSONFactory_BERLIN_GROUP_1_3Test.scala @@ -100,10 +100,6 @@ class JSONFactory_BERLIN_GROUP_1_3Test extends FeatureSpec with Matchers with Gi val result = JSONFactory_BERLIN_GROUP_1_3.createTransactionJSON(transaction) result.transactionId shouldBe transaction.id.value - result.creditorName shouldBe None //Some("Creditor Name") - result.creditorAccount shouldBe None - result.debtorName shouldBe None//Some(bankAccount.name) - result.debtorAccount shouldBe None result.transactionAmount.currency shouldBe transaction.currency.get result.bookingDate should not be empty @@ -112,8 +108,8 @@ class JSONFactory_BERLIN_GROUP_1_3Test extends FeatureSpec with Matchers with Gi val jsonString: String = compactRender(Extraction.decompose(result)) - jsonString.contains("creditorName") shouldBe false - jsonString.contains("creditorAccount") shouldBe false + jsonString.contains("creditorName") shouldBe true + jsonString.contains("creditorAccount") shouldBe true jsonString.contains("debtorName") shouldBe false jsonString.contains("debtorAccount") shouldBe false diff --git a/obp-api/src/test/scala/code/util/PasswordUtilTest.scala b/obp-api/src/test/scala/code/util/PasswordUtilTest.scala new file mode 100644 index 000000000..b45e6c6ce --- /dev/null +++ b/obp-api/src/test/scala/code/util/PasswordUtilTest.scala @@ -0,0 +1,85 @@ +/** +Open Bank Project - API +Copyright (C) 2011-2019, TESOBE GmbH. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . + +Email: contact@tesobe.com +TESOBE GmbH. +Osloer Strasse 16/17 +Berlin 13359, Germany + +This product includes software developed at +TESOBE (http://www.tesobe.com/) + */ + +package code.api.util + +import code.util.Helper.MdcLoggable +import org.scalatest.{FeatureSpec, GivenWhenThen, Matchers} + +class PasswordUtilTest extends FeatureSpec with Matchers with GivenWhenThen with MdcLoggable { + + feature("Evaluate password strength using Zxcvbn") { + + scenario("Very weak password should return low score and be unacceptable") { + Given("a common password '12345678'") + val password = "12345678" + + When("measured with zxcvbn") + val strength = PasswordUtil.getStrength(password) + + Then("the score should be 0 and it should be unacceptable") + strength.getScore should be <= 1 + PasswordUtil.isAcceptable(password) should be (false) + } + + scenario("Moderate password should be acceptable") { + Given("a moderately strong password 'OpenBank2025$'") + val password = "OpenBank2025$" + + When("measured with zxcvbn") + val strength = PasswordUtil.getStrength(password) + + Then("the score should be >= 3 and it should be acceptable") + strength.getScore should be >= 3 + PasswordUtil.isAcceptable(password) should be (true) + } + + scenario("Strong password with emoji and unicode should be acceptable") { + Given("a complex password '🔥MySecurę密码2025!'") + val password = "🔥MySecurę密码2025!" + + When("measured with zxcvbn") + val strength = PasswordUtil.getStrength(password) + + Then("the score should be >= 3 and it should be acceptable") + strength.getScore should be >= 3 + PasswordUtil.isAcceptable(password) should be (true) + } + + scenario("Very strong password should be clearly acceptable") { + Given("a very strong password 'G@lacticSafe#AlphaZebra99!!'") + val password = "G@lacticSafe#AlphaZebra99!!" + + When("measured with zxcvbn") + val strength = PasswordUtil.getStrength(password) + + Then("the score should be 4 and it should be acceptable") + strength.getScore should be (4) + PasswordUtil.isAcceptable(password) should be (true) + } + + } +} diff --git a/obp-commons/src/main/scala/com/openbankproject/commons/model/enums/Enumerations.scala b/obp-commons/src/main/scala/com/openbankproject/commons/model/enums/Enumerations.scala index 6de788ee3..a0ae6ca04 100644 --- a/obp-commons/src/main/scala/com/openbankproject/commons/model/enums/Enumerations.scala +++ b/obp-commons/src/main/scala/com/openbankproject/commons/model/enums/Enumerations.scala @@ -173,6 +173,14 @@ object PemCertificateRole extends OBPEnumeration[PemCertificateRole] { object PSP_IC extends Value object PSP_AI extends Value object PSP_PI extends Value + + def toBerlinGroup(role: String): String = { + role match { + case item if PSP_AI.toString == item => "AISP" + case item if PSP_PI.toString == item => "PISP" + case _ => "" + } + } } sealed trait UserInvitationPurpose extends EnumValue