From 9b41093c1ac83de49208d37b6fbe6ca5af078953 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Jul 2023 15:32:06 +0000 Subject: [PATCH 01/75] Bump guava from 31.1-jre to 32.0.0-jre in /obp-commons Bumps [guava](https://github.com/google/guava) from 31.1-jre to 32.0.0-jre. - [Release notes](https://github.com/google/guava/releases) - [Commits](https://github.com/google/guava/commits) --- updated-dependencies: - dependency-name: com.google.guava:guava dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- obp-commons/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/obp-commons/pom.xml b/obp-commons/pom.xml index 6f28d7323..c7f68bad4 100644 --- a/obp-commons/pom.xml +++ b/obp-commons/pom.xml @@ -75,7 +75,7 @@ com.google.guava guava - 31.1-jre + 32.0.0-jre From 6e45bef25807455183a4cf04f26ae35704a6cf8c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Sep 2023 19:36:28 +0000 Subject: [PATCH 02/75] Bump org.apache.commons:commons-compress in /obp-api Bumps org.apache.commons:commons-compress from 1.23.0 to 1.24.0. --- updated-dependencies: - dependency-name: org.apache.commons:commons-compress dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- obp-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/obp-api/pom.xml b/obp-api/pom.xml index 846196e01..993850cbb 100644 --- a/obp-api/pom.xml +++ b/obp-api/pom.xml @@ -266,7 +266,7 @@ org.apache.commons commons-compress - 1.23.0 + 1.24.0 com.twitter From 5ff89e8d5972c76aaf097d9df711be3fb100ce95 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Nov 2023 20:58:20 +0000 Subject: [PATCH 03/75] Bump org.elasticsearch:elasticsearch from 8.8.1 to 8.10.3 in /obp-api Bumps [org.elasticsearch:elasticsearch](https://github.com/elastic/elasticsearch) from 8.8.1 to 8.10.3. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Changelog](https://github.com/elastic/elasticsearch/blob/main/CHANGELOG.md) - [Commits](https://github.com/elastic/elasticsearch/compare/v8.8.1...v8.10.3) --- updated-dependencies: - dependency-name: org.elasticsearch:elasticsearch dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- obp-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/obp-api/pom.xml b/obp-api/pom.xml index 2ac5abb1a..f17200ecd 100644 --- a/obp-api/pom.xml +++ b/obp-api/pom.xml @@ -212,7 +212,7 @@ org.elasticsearch elasticsearch - 8.8.1 + 8.10.3 From dba673f868b5ebc16503a1b325f3d8be3d367ef4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Dec 2023 22:20:41 +0000 Subject: [PATCH 04/75] Bump ch.qos.logback:logback-classic from 1.2.3 to 1.2.13 in /obp-api Bumps [ch.qos.logback:logback-classic](https://github.com/qos-ch/logback) from 1.2.3 to 1.2.13. - [Commits](https://github.com/qos-ch/logback/compare/v_1.2.3...v_1.2.13) --- updated-dependencies: - dependency-name: ch.qos.logback:logback-classic dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- obp-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/obp-api/pom.xml b/obp-api/pom.xml index 714d8986c..e01f3cd21 100644 --- a/obp-api/pom.xml +++ b/obp-api/pom.xml @@ -58,7 +58,7 @@ ch.qos.logback logback-classic - 1.2.3 + 1.2.13 net.liftweb From 52c8ac2384d12e05cd16fd089ab09c762734e1eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Mon, 13 May 2024 16:51:41 +0200 Subject: [PATCH 05/75] feature/At login should check user agreement hash --- .../code/model/dataAccess/AuthUser.scala | 29 ++++--- .../code/snippet/TermsAndConditions.scala | 86 +++++++++++++++++++ .../main/scala/code/users/UserAgreement.scala | 8 ++ obp-api/src/main/scala/code/util/Helper.scala | 2 +- .../src/main/webapp/terms-and-conditions.html | 12 ++- 5 files changed, 125 insertions(+), 12 deletions(-) create mode 100644 obp-api/src/main/scala/code/snippet/TermsAndConditions.scala diff --git a/obp-api/src/main/scala/code/model/dataAccess/AuthUser.scala b/obp-api/src/main/scala/code/model/dataAccess/AuthUser.scala index c5e3a8947..c3eb29690 100644 --- a/obp-api/src/main/scala/code/model/dataAccess/AuthUser.scala +++ b/obp-api/src/main/scala/code/model/dataAccess/AuthUser.scala @@ -1038,14 +1038,23 @@ def restoreSomeSessions(): Unit = { override def login: NodeSeq = { // This query parameter is specific to ORY Hydra login request val loginChallenge: Box[String] = ObpS.param("login_challenge").or(S.getSessionAttribute("login_challenge")) - def redirectUri(): String = { - loginRedirect.get match { - case Full(url) => - loginRedirect(Empty) - url - case _ => - homePage + def redirectUri(user: Box[ResourceUser]): String = { + val userId = user.map(_.userId).getOrElse("") + val hashedAgreementTextOfUser = UserAgreementProvider.userAgreementProvider.vend.getUserAgreement(userId, "terms_and_conditions").map(_.agreementHash).getOrElse(HashUtil.Sha256Hash("not set")) + val agreementText = getWebUiPropsValue("webui_terms_and_conditions", "not set") + val hashedAgreementText = HashUtil.Sha256Hash(agreementText) + if(hashedAgreementTextOfUser == hashedAgreementText) { + loginRedirect.get match { + case Full(url) => + loginRedirect(Empty) + url + case _ => + homePage + } + } else { + "/terms-and-conditions" } + } //Check the internal redirect, in case for open redirect issue. // variable redirect is from loginRedirect, it is set-up in OAuthAuthorisation.scala as following code: @@ -1131,7 +1140,7 @@ def restoreSomeSessions(): Unit = { // User init actions AfterApiAuth.innerLoginUserInitAction(Full(user)) logger.info("login redirect: " + loginRedirect.get) - val redirect = redirectUri() + val redirect = redirectUri(user.user.foreign) checkInternalRedirectAndLogUserIn(preLoginState, redirect, user) } else { // If user is NOT locked AND password is wrong => increment bad login attempt counter. LoginAttempt.incrementBadLoginAttempts(user.getProvider(),usernameFromGui) @@ -1151,7 +1160,7 @@ def restoreSomeSessions(): Unit = { LoginAttempt.resetBadLoginAttempts(user.getProvider(), usernameFromGui) val preLoginState = capturePreLoginState() logger.info("login redirect: " + loginRedirect.get) - val redirect = redirectUri() + val redirect = redirectUri(user.user.foreign) //This method is used for connector = kafka* || obpjvm* //It will update the views and createAccountHolder .... registeredUserHelper(user.getProvider(),user.username.get) @@ -1176,7 +1185,7 @@ def restoreSomeSessions(): Unit = { val preLoginState = capturePreLoginState() logger.info("login redirect: " + loginRedirect.get) - val redirect = redirectUri() + val redirect = redirectUri(user.foreign) externalUserHelper(usernameFromGui, passwordFromGui) match { case Full(user: AuthUser) => LoginAttempt.resetBadLoginAttempts(user.getProvider(), usernameFromGui) diff --git a/obp-api/src/main/scala/code/snippet/TermsAndConditions.scala b/obp-api/src/main/scala/code/snippet/TermsAndConditions.scala new file mode 100644 index 000000000..6562c4e9e --- /dev/null +++ b/obp-api/src/main/scala/code/snippet/TermsAndConditions.scala @@ -0,0 +1,86 @@ +/** +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.snippet + +import code.api.Constant +import code.api.Constant.localIdentityProvider +import code.api.util.{APIUtil, HashUtil, SecureRandomUtil} +import code.model.dataAccess.{AuthUser, ResourceUser} +import code.users +import code.users.{UserAgreementProvider, UserInvitationProvider, Users} +import code.util.Helper +import code.util.Helper.{MdcLoggable, ObpS} +import code.webuiprops.MappedWebUiPropsProvider.getWebUiPropsValue +import com.openbankproject.commons.model.User +import net.liftweb.common.{Box, Failure, Full} +import net.liftweb.http.{RequestVar, S, SHtml} +import net.liftweb.util.CssSel +import net.liftweb.util.Helpers._ + +import java.time.{Duration, ZoneId, ZoneOffset, ZonedDateTime} +import java.util.Date + +class TermsAndConditions extends MdcLoggable { + + private object firstNameVar extends RequestVar("") + private object lastNameVar extends RequestVar("") + private object companyVar extends RequestVar("") + private object countryVar extends RequestVar("None") + private object devEmailVar extends RequestVar("") + private object usernameVar extends RequestVar("") + private object consentForCollectingMandatoryCheckboxVar extends RequestVar(true) + private object privacyCheckboxVar extends RequestVar(false) + + def updateForm: CssSel = { + + def submitButtonDefense(): Unit = { + updateUserAgreement() + } + + def skipButtonDefense(): Unit = { + S.redirectTo("/") + } + + def update = { + "type=submit" #> SHtml.submit(s"Accept", () => submitButtonDefense) & + "type=reset" #> SHtml.submit(s"Skip", () => skipButtonDefense) + } + update + } + + private def updateUserAgreement() = { + if(AuthUser.currentUser.isDefined) { + val agreementText = getWebUiPropsValue("webui_terms_and_conditions", "not set") + // val hashedAgreementText = HashUtil.Sha256Hash(agreementText) + UserAgreementProvider.userAgreementProvider.vend.createOrUpdateUserAgreement( + AuthUser.currentUser.flatMap(_.user.foreign.map(_.userId)).getOrElse(""), "terms_and_conditions", agreementText) + S.redirectTo("/") + } + + } + +} diff --git a/obp-api/src/main/scala/code/users/UserAgreement.scala b/obp-api/src/main/scala/code/users/UserAgreement.scala index 19a643279..cf52c203e 100644 --- a/obp-api/src/main/scala/code/users/UserAgreement.scala +++ b/obp-api/src/main/scala/code/users/UserAgreement.scala @@ -7,6 +7,7 @@ import code.api.util.HashUtil import code.util.UUIDString import net.liftweb.common.{Box, Empty, Full} import net.liftweb.mapper._ +import net.liftweb.common.Box.tryo object MappedUserAgreementProvider extends UserAgreementProvider { override def createOrUpdateUserAgreement(userId: String, agreementType: String, agreementText: String): Box[UserAgreement] = { @@ -75,5 +76,12 @@ class UserAgreement extends UserAgreementTrait with LongKeyedMapper[UserAgreemen object UserAgreement extends UserAgreement with LongKeyedMetaMapper[UserAgreement] { override def dbIndexes: List[BaseIndex[UserAgreement]] = UniqueIndex(UserAgreementId) :: super.dbIndexes + override def beforeSave = List( + agreement => + tryo { + agreement.AgreementHash( HashUtil.Sha256Hash(agreement.agreementText)).save + } + ) + } diff --git a/obp-api/src/main/scala/code/util/Helper.scala b/obp-api/src/main/scala/code/util/Helper.scala index fa3cd2aa9..2bea5c961 100644 --- a/obp-api/src/main/scala/code/util/Helper.scala +++ b/obp-api/src/main/scala/code/util/Helper.scala @@ -211,7 +211,7 @@ object Helper extends Loggable { */ def isValidInternalRedirectUrl(url: String) : Boolean = { //set the default value is "/" and "/oauth/authorize" - val validUrls = List("/","/oauth/authorize","/consumer-registration","/dummy-user-tokens","/create-sandbox-account", "/add-user-auth-context-update-request","/otp") + val validUrls = List("/","/oauth/authorize","/consumer-registration","/dummy-user-tokens","/create-sandbox-account", "/add-user-auth-context-update-request","/otp","/terms-and-conditions") //case1: OBP-API login: url = "/" //case2: API-Explore oauth login: url = "/oauth/authorize?oauth_token=V0JTCDYXWUNTXDZ3VUDNM1HE3Q1PZR2WJ4PURXQA&logUserOut=false" diff --git a/obp-api/src/main/webapp/terms-and-conditions.html b/obp-api/src/main/webapp/terms-and-conditions.html index 0c653baf8..c1fe7a94a 100644 --- a/obp-api/src/main/webapp/terms-and-conditions.html +++ b/obp-api/src/main/webapp/terms-and-conditions.html @@ -26,7 +26,17 @@ Berlin 13359, Germany
-
+
+
+
+ +
+
+ +
+
+
+
From 255f5025f09b3a14568ef9e9b4ab25f2c3d0e350 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Tue, 14 May 2024 10:24:33 +0200 Subject: [PATCH 06/75] feature/At login should check user agreement hash part 2 --- .../code/model/dataAccess/AuthUser.scala | 27 +++++--- .../scala/code/snippet/PrivacyPolicy.scala | 65 +++++++++++++++++++ .../code/snippet/TermsAndConditions.scala | 28 ++------ .../main/scala/code/users/UserAgreement.scala | 3 +- obp-api/src/main/scala/code/util/Helper.scala | 7 +- obp-api/src/main/webapp/privacy-policy.html | 12 +++- .../src/main/webapp/terms-and-conditions.html | 2 +- 7 files changed, 108 insertions(+), 36 deletions(-) create mode 100644 obp-api/src/main/scala/code/snippet/PrivacyPolicy.scala diff --git a/obp-api/src/main/scala/code/model/dataAccess/AuthUser.scala b/obp-api/src/main/scala/code/model/dataAccess/AuthUser.scala index c3eb29690..6ba53c07e 100644 --- a/obp-api/src/main/scala/code/model/dataAccess/AuthUser.scala +++ b/obp-api/src/main/scala/code/model/dataAccess/AuthUser.scala @@ -1040,16 +1040,27 @@ def restoreSomeSessions(): Unit = { val loginChallenge: Box[String] = ObpS.param("login_challenge").or(S.getSessionAttribute("login_challenge")) def redirectUri(user: Box[ResourceUser]): String = { val userId = user.map(_.userId).getOrElse("") - val hashedAgreementTextOfUser = UserAgreementProvider.userAgreementProvider.vend.getUserAgreement(userId, "terms_and_conditions").map(_.agreementHash).getOrElse(HashUtil.Sha256Hash("not set")) + val hashedAgreementTextOfUser = + UserAgreementProvider.userAgreementProvider.vend.getUserAgreement(userId, "terms_and_conditions") + .map(_.agreementHash).getOrElse(HashUtil.Sha256Hash("not set")) val agreementText = getWebUiPropsValue("webui_terms_and_conditions", "not set") val hashedAgreementText = HashUtil.Sha256Hash(agreementText) - if(hashedAgreementTextOfUser == hashedAgreementText) { - loginRedirect.get match { - case Full(url) => - loginRedirect(Empty) - url - case _ => - homePage + if(hashedAgreementTextOfUser == hashedAgreementText) { // Chech terms and conditions + val hashedAgreementTextOfUser = + UserAgreementProvider.userAgreementProvider.vend.getUserAgreement(userId, "privacy_conditions") + .map(_.agreementHash).getOrElse(HashUtil.Sha256Hash("not set")) + val agreementText = getWebUiPropsValue("webui_privacy_policy", "not set") + val hashedAgreementText = HashUtil.Sha256Hash(agreementText) + if(hashedAgreementTextOfUser == hashedAgreementText) { // Check privacy policy + loginRedirect.get match { + case Full(url) => + loginRedirect(Empty) + url + case _ => + homePage + } + } else { + "/privacy-policy" } } else { "/terms-and-conditions" diff --git a/obp-api/src/main/scala/code/snippet/PrivacyPolicy.scala b/obp-api/src/main/scala/code/snippet/PrivacyPolicy.scala new file mode 100644 index 000000000..87098e813 --- /dev/null +++ b/obp-api/src/main/scala/code/snippet/PrivacyPolicy.scala @@ -0,0 +1,65 @@ +/** +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.snippet + +import code.model.dataAccess.AuthUser +import code.users.UserAgreementProvider +import code.util.Helper.MdcLoggable +import code.webuiprops.MappedWebUiPropsProvider.getWebUiPropsValue +import net.liftweb.http.{S, SHtml} +import net.liftweb.util.CssSel +import net.liftweb.util.Helpers._ + +class PrivacyPolicy extends MdcLoggable { + + def updateForm: CssSel = { + + def submitButtonDefense(): Unit = { + updateUserAgreement() + } + + def skipButtonDefense(): Unit = { + S.redirectTo("/") + } + + def update = { + "type=submit" #> SHtml.submit(s"Accept", () => submitButtonDefense) & + "type=reset" #> SHtml.submit(s"Skip", () => skipButtonDefense) + } + update + } + + private def updateUserAgreement() = { + if(AuthUser.currentUser.isDefined) { + val agreementText = getWebUiPropsValue("webui_privacy_policy", "not set") + UserAgreementProvider.userAgreementProvider.vend.createOrUpdateUserAgreement( + AuthUser.currentUser.flatMap(_.user.foreign.map(_.userId)).getOrElse(""), "privacy_conditions", agreementText) + S.redirectTo("/") + } + } + +} diff --git a/obp-api/src/main/scala/code/snippet/TermsAndConditions.scala b/obp-api/src/main/scala/code/snippet/TermsAndConditions.scala index 6562c4e9e..4ea475663 100644 --- a/obp-api/src/main/scala/code/snippet/TermsAndConditions.scala +++ b/obp-api/src/main/scala/code/snippet/TermsAndConditions.scala @@ -26,35 +26,16 @@ TESOBE (http://www.tesobe.com/) */ package code.snippet -import code.api.Constant -import code.api.Constant.localIdentityProvider -import code.api.util.{APIUtil, HashUtil, SecureRandomUtil} -import code.model.dataAccess.{AuthUser, ResourceUser} -import code.users -import code.users.{UserAgreementProvider, UserInvitationProvider, Users} -import code.util.Helper -import code.util.Helper.{MdcLoggable, ObpS} +import code.model.dataAccess.AuthUser +import code.users.UserAgreementProvider +import code.util.Helper.MdcLoggable import code.webuiprops.MappedWebUiPropsProvider.getWebUiPropsValue -import com.openbankproject.commons.model.User -import net.liftweb.common.{Box, Failure, Full} -import net.liftweb.http.{RequestVar, S, SHtml} +import net.liftweb.http.{S, SHtml} import net.liftweb.util.CssSel import net.liftweb.util.Helpers._ -import java.time.{Duration, ZoneId, ZoneOffset, ZonedDateTime} -import java.util.Date - class TermsAndConditions extends MdcLoggable { - private object firstNameVar extends RequestVar("") - private object lastNameVar extends RequestVar("") - private object companyVar extends RequestVar("") - private object countryVar extends RequestVar("None") - private object devEmailVar extends RequestVar("") - private object usernameVar extends RequestVar("") - private object consentForCollectingMandatoryCheckboxVar extends RequestVar(true) - private object privacyCheckboxVar extends RequestVar(false) - def updateForm: CssSel = { def submitButtonDefense(): Unit = { @@ -80,7 +61,6 @@ class TermsAndConditions extends MdcLoggable { AuthUser.currentUser.flatMap(_.user.foreign.map(_.userId)).getOrElse(""), "terms_and_conditions", agreementText) S.redirectTo("/") } - } } diff --git a/obp-api/src/main/scala/code/users/UserAgreement.scala b/obp-api/src/main/scala/code/users/UserAgreement.scala index cf52c203e..e4927e0e9 100644 --- a/obp-api/src/main/scala/code/users/UserAgreement.scala +++ b/obp-api/src/main/scala/code/users/UserAgreement.scala @@ -79,7 +79,8 @@ object UserAgreement extends UserAgreement with LongKeyedMetaMapper[UserAgreemen override def beforeSave = List( agreement => tryo { - agreement.AgreementHash( HashUtil.Sha256Hash(agreement.agreementText)).save + val hash = HashUtil.Sha256Hash(agreement.agreementText) + agreement.AgreementHash(hash ).save } ) diff --git a/obp-api/src/main/scala/code/util/Helper.scala b/obp-api/src/main/scala/code/util/Helper.scala index 2bea5c961..f1ffe85d6 100644 --- a/obp-api/src/main/scala/code/util/Helper.scala +++ b/obp-api/src/main/scala/code/util/Helper.scala @@ -211,7 +211,12 @@ object Helper extends Loggable { */ def isValidInternalRedirectUrl(url: String) : Boolean = { //set the default value is "/" and "/oauth/authorize" - val validUrls = List("/","/oauth/authorize","/consumer-registration","/dummy-user-tokens","/create-sandbox-account", "/add-user-auth-context-update-request","/otp","/terms-and-conditions") + val validUrls = List( + "/","/oauth/authorize","/consumer-registration", + "/dummy-user-tokens","/create-sandbox-account", + "/add-user-auth-context-update-request","/otp", + "/terms-and-conditions", "/privacy-policy" + ) //case1: OBP-API login: url = "/" //case2: API-Explore oauth login: url = "/oauth/authorize?oauth_token=V0JTCDYXWUNTXDZ3VUDNM1HE3Q1PZR2WJ4PURXQA&logUserOut=false" diff --git a/obp-api/src/main/webapp/privacy-policy.html b/obp-api/src/main/webapp/privacy-policy.html index d831ae849..3f00ed2ff 100644 --- a/obp-api/src/main/webapp/privacy-policy.html +++ b/obp-api/src/main/webapp/privacy-policy.html @@ -26,7 +26,17 @@ Berlin 13359, Germany
-
+
+
+
+ +
+
+ +
+
+
+
diff --git a/obp-api/src/main/webapp/terms-and-conditions.html b/obp-api/src/main/webapp/terms-and-conditions.html index c1fe7a94a..806846e1a 100644 --- a/obp-api/src/main/webapp/terms-and-conditions.html +++ b/obp-api/src/main/webapp/terms-and-conditions.html @@ -26,7 +26,7 @@ Berlin 13359, Germany
-
+
From b8cc00e3d04242f93e4b4ced74d5a7cec82fd8d6 Mon Sep 17 00:00:00 2001 From: Simon Redfern Date: Tue, 14 May 2024 12:39:31 +0200 Subject: [PATCH 07/75] update of main-faq.html --- obp-api/src/main/webapp/main-faq.html | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/obp-api/src/main/webapp/main-faq.html b/obp-api/src/main/webapp/main-faq.html index 6192b9ce2..6a65bbecd 100644 --- a/obp-api/src/main/webapp/main-faq.html +++ b/obp-api/src/main/webapp/main-faq.html @@ -51,11 +51,10 @@

- There are two ways to authenticate a user:There are multiple ways to authenticate a user including: OAuth and Direct Login . If you - are using this sandbox for a hackathon, we recommend you use Direct Login to authenticate as it - is easier than the OAuth workflow. + are using this sandbox for a hackathon, we recommend you use Direct Login to start with.

@@ -78,10 +77,10 @@ href="">Direct Login. For an OAuth walkthrough example with sample code, please see here. - We use OAuth 1.0a. For deepish technical details of the flow We support OAuth 1.0a. For deepish technical details of the flow see here.
- We also support OAuth 2.0. For the technical details of using OBP API with OAuth 2.0, please We support OAuth 2.0. For the technical details of using OBP API with OAuth 2.0, please see here.

From b4fb9fa555ada75ffcbd97b07783826ffabc6f40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Tue, 14 May 2024 13:27:47 +0200 Subject: [PATCH 08/75] feature/At login should check user agreement hash part 3 --- .../src/main/scala/code/snippet/PrivacyPolicy.scala | 13 ++++++++++++- .../scala/code/snippet/TermsAndConditions.scala | 13 ++++++++++++- obp-api/src/main/webapp/privacy-policy.html | 5 +++-- obp-api/src/main/webapp/terms-and-conditions.html | 5 +++-- 4 files changed, 30 insertions(+), 6 deletions(-) diff --git a/obp-api/src/main/scala/code/snippet/PrivacyPolicy.scala b/obp-api/src/main/scala/code/snippet/PrivacyPolicy.scala index 87098e813..436c243aa 100644 --- a/obp-api/src/main/scala/code/snippet/PrivacyPolicy.scala +++ b/obp-api/src/main/scala/code/snippet/PrivacyPolicy.scala @@ -46,9 +46,20 @@ class PrivacyPolicy extends MdcLoggable { S.redirectTo("/") } + def displayContent = { + if(AuthUser.currentUser.isDefined) { + "block" + } else { + "none" + } + } + def update = { + val username = AuthUser.currentUser.flatMap(_.user.foreign.map(_.name)).getOrElse("") + "#privacy-policy-username *" #> username & "type=submit" #> SHtml.submit(s"Accept", () => submitButtonDefense) & - "type=reset" #> SHtml.submit(s"Skip", () => skipButtonDefense) + "type=reset" #> SHtml.submit(s"Skip", () => skipButtonDefense) & + "#form_privacy_policy [style]" #> s"display: $displayContent;" } update } diff --git a/obp-api/src/main/scala/code/snippet/TermsAndConditions.scala b/obp-api/src/main/scala/code/snippet/TermsAndConditions.scala index 4ea475663..998e57bc9 100644 --- a/obp-api/src/main/scala/code/snippet/TermsAndConditions.scala +++ b/obp-api/src/main/scala/code/snippet/TermsAndConditions.scala @@ -46,9 +46,20 @@ class TermsAndConditions extends MdcLoggable { S.redirectTo("/") } + def displayContent = { + if(AuthUser.currentUser.isDefined) { + "block" + } else { + "none" + } + } + def update = { + val username = AuthUser.currentUser.flatMap(_.user.foreign.map(_.name)).getOrElse("") + "#terms-and-conditions-username *" #> username & "type=submit" #> SHtml.submit(s"Accept", () => submitButtonDefense) & - "type=reset" #> SHtml.submit(s"Skip", () => skipButtonDefense) + "type=reset" #> SHtml.submit(s"Skip", () => skipButtonDefense) & + "#form_terms_and_conditions [style]" #> s"display: $displayContent;" } update } diff --git a/obp-api/src/main/webapp/privacy-policy.html b/obp-api/src/main/webapp/privacy-policy.html index 3f00ed2ff..d2c7ba7c7 100644 --- a/obp-api/src/main/webapp/privacy-policy.html +++ b/obp-api/src/main/webapp/privacy-policy.html @@ -26,8 +26,9 @@ Berlin 13359, Germany
- -
+ +
+
Dear . We have updated our terms since you last agreed to them! Please review the text and agree if you agree!
diff --git a/obp-api/src/main/webapp/terms-and-conditions.html b/obp-api/src/main/webapp/terms-and-conditions.html index 806846e1a..895b4d64a 100644 --- a/obp-api/src/main/webapp/terms-and-conditions.html +++ b/obp-api/src/main/webapp/terms-and-conditions.html @@ -26,8 +26,9 @@ Berlin 13359, Germany
- -
+ +
+
Dear . We have updated our terms since you last agreed to them! Please review the text and agree if you agree!
From 4376c92f3b9d565fa0ea110bf48a596bb6d1a19d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Wed, 15 May 2024 09:00:57 +0200 Subject: [PATCH 09/75] feature/At login should check user agreement hash, Add i18n support --- .../src/main/resources/i18n/lift-core.properties | 14 +++++++++++++- .../main/scala/code/snippet/PrivacyPolicy.scala | 5 +++-- .../scala/code/snippet/TermsAndConditions.scala | 5 +++-- obp-api/src/main/webapp/privacy-policy.html | 2 +- obp-api/src/main/webapp/terms-and-conditions.html | 2 +- 5 files changed, 21 insertions(+), 7 deletions(-) diff --git a/obp-api/src/main/resources/i18n/lift-core.properties b/obp-api/src/main/resources/i18n/lift-core.properties index 3a325f7be..4f7d69d1f 100644 --- a/obp-api/src/main/resources/i18n/lift-core.properties +++ b/obp-api/src/main/resources/i18n/lift-core.properties @@ -385,4 +385,16 @@ your.secret.link.is.not.valid = Looks like the invitation link is invalid. Still privacy_policy_checkbox_text =I agree to the above Privacy Policy privacy_policy_checkbox_label = Privacy Policy terms_and_conditions_checkbox_text = I agree to the above Terms and Conditions -terms_and_conditions_checkbox_label = Terms and Conditions \ No newline at end of file +terms_and_conditions_checkbox_label = Terms and Conditions + +# Terms and conditions page +outdated.terms.button.skip = Skip +outdated.terms.button.accept = Accept +outdated.terms.warning.1 = Dear +outdated.terms.warning.2 = . We have updated our terms since you last agreed to them! Please review the text and agree if you agree! + +# Privacy policy page +outdated.policy.button.skip = Skip +outdated.policy.button.accept = Accept +outdated.policy.warning.1 = Dear +outdated.policy.warning.2 = . We have updated our policy since you last agreed to them! Please review the text and agree if you agree! \ No newline at end of file diff --git a/obp-api/src/main/scala/code/snippet/PrivacyPolicy.scala b/obp-api/src/main/scala/code/snippet/PrivacyPolicy.scala index 436c243aa..1ff3fd0e7 100644 --- a/obp-api/src/main/scala/code/snippet/PrivacyPolicy.scala +++ b/obp-api/src/main/scala/code/snippet/PrivacyPolicy.scala @@ -28,6 +28,7 @@ package code.snippet import code.model.dataAccess.AuthUser import code.users.UserAgreementProvider +import code.util.Helper import code.util.Helper.MdcLoggable import code.webuiprops.MappedWebUiPropsProvider.getWebUiPropsValue import net.liftweb.http.{S, SHtml} @@ -57,8 +58,8 @@ class PrivacyPolicy extends MdcLoggable { def update = { val username = AuthUser.currentUser.flatMap(_.user.foreign.map(_.name)).getOrElse("") "#privacy-policy-username *" #> username & - "type=submit" #> SHtml.submit(s"Accept", () => submitButtonDefense) & - "type=reset" #> SHtml.submit(s"Skip", () => skipButtonDefense) & + "type=submit" #> SHtml.submit(s"${Helper.i18n("outdated.terms.button.accept")}", () => submitButtonDefense) & + "type=reset" #> SHtml.submit(s"${Helper.i18n("outdated.terms.button.skip")}", () => skipButtonDefense) & "#form_privacy_policy [style]" #> s"display: $displayContent;" } update diff --git a/obp-api/src/main/scala/code/snippet/TermsAndConditions.scala b/obp-api/src/main/scala/code/snippet/TermsAndConditions.scala index 998e57bc9..5bbaa754a 100644 --- a/obp-api/src/main/scala/code/snippet/TermsAndConditions.scala +++ b/obp-api/src/main/scala/code/snippet/TermsAndConditions.scala @@ -28,6 +28,7 @@ package code.snippet import code.model.dataAccess.AuthUser import code.users.UserAgreementProvider +import code.util.Helper import code.util.Helper.MdcLoggable import code.webuiprops.MappedWebUiPropsProvider.getWebUiPropsValue import net.liftweb.http.{S, SHtml} @@ -57,8 +58,8 @@ class TermsAndConditions extends MdcLoggable { def update = { val username = AuthUser.currentUser.flatMap(_.user.foreign.map(_.name)).getOrElse("") "#terms-and-conditions-username *" #> username & - "type=submit" #> SHtml.submit(s"Accept", () => submitButtonDefense) & - "type=reset" #> SHtml.submit(s"Skip", () => skipButtonDefense) & + "type=submit" #> SHtml.submit(s"${Helper.i18n("outdated.policy.button.accept")}", () => submitButtonDefense) & + "type=reset" #> SHtml.submit(s"${Helper.i18n("outdated.policy.button.skip")}", () => skipButtonDefense) & "#form_terms_and_conditions [style]" #> s"display: $displayContent;" } update diff --git a/obp-api/src/main/webapp/privacy-policy.html b/obp-api/src/main/webapp/privacy-policy.html index d2c7ba7c7..683b9ad47 100644 --- a/obp-api/src/main/webapp/privacy-policy.html +++ b/obp-api/src/main/webapp/privacy-policy.html @@ -28,7 +28,7 @@ Berlin 13359, Germany
-
Dear . We have updated our terms since you last agreed to them! Please review the text and agree if you agree!
+
Dear . We have updated our policy since you last agreed to them! Please review the text and agree if you agree!
diff --git a/obp-api/src/main/webapp/terms-and-conditions.html b/obp-api/src/main/webapp/terms-and-conditions.html index 895b4d64a..714d93f00 100644 --- a/obp-api/src/main/webapp/terms-and-conditions.html +++ b/obp-api/src/main/webapp/terms-and-conditions.html @@ -28,7 +28,7 @@ Berlin 13359, Germany
-
Dear . We have updated our terms since you last agreed to them! Please review the text and agree if you agree!
+
Dear . We have updated our terms since you last agreed to them! Please review the text and agree if you agree!
From 93fea945ae9caa062b0fb0a49e8cdc945fb0b2b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Thu, 16 May 2024 16:27:25 +0200 Subject: [PATCH 10/75] feature/Add endpoint getBankAccountBalances v5.1.0 --- .../scala/code/api/v4_0_0/APIMethods400.scala | 6 ++-- .../scala/code/api/v5_1_0/APIMethods510.scala | 32 +++++++++++++++++++ .../code/api/v4_0_0/AccountBalanceTest.scala | 2 +- .../code/api/v5_1_0/AccountBalanceTest.scala | 32 +++++++++++++++++++ 4 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 obp-api/src/test/scala/code/api/v5_1_0/AccountBalanceTest.scala diff --git a/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala b/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala index da72ce71f..aa8631ae8 100644 --- a/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala +++ b/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala @@ -3373,9 +3373,9 @@ trait APIMethods400 extends MdcLoggable { } staticResourceDocs += ResourceDoc( - getBankAccountBalances, + getBankAccountBalancesForCurrentUser, implementedInApiVersion, - nameOf(getBankAccountBalances), + nameOf(getBankAccountBalancesForCurrentUser), "GET", "/banks/BANK_ID/accounts/ACCOUNT_ID/balances", "Get Account Balances", @@ -3386,7 +3386,7 @@ trait APIMethods400 extends MdcLoggable { apiTagAccount :: apiTagPSD2AIS :: apiTagPsd2 :: Nil ) - lazy val getBankAccountBalances : OBPEndpoint = { + lazy val getBankAccountBalancesForCurrentUser : OBPEndpoint = { case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: "balances" :: Nil JsonGet _ => { cc => implicit val ec = EndpointContext(Some(cc)) for { diff --git a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala index 97acaf104..fec1deec9 100644 --- a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala +++ b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala @@ -19,6 +19,7 @@ import code.api.v3_0_0.JSONFactory300 import code.api.v3_0_0.JSONFactory300.createAggregateMetricJson import code.api.v3_1_0.ConsentJsonV310 import code.api.v3_1_0.JSONFactory310.createBadLoginStatusJson +import code.api.v4_0_0.JSONFactory400.createAccountBalancesJson import code.api.v4_0_0.{JSONFactory400, PostAccountAccessJsonV400, PostApiCollectionJson400, RevokedJsonV400} import code.api.v5_1_0.JSONFactory510.{createRegulatedEntitiesJson, createRegulatedEntityJson} import code.atmattribute.AtmAttribute @@ -2153,7 +2154,38 @@ trait APIMethods510 { } } + staticResourceDocs += ResourceDoc( + getBankAccountBalances, + implementedInApiVersion, + nameOf(getBankAccountBalances), + "GET", + "/banks/BANK_ID/accounts/ACCOUNT_ID/views/VIEW_ID/balances", + "Get Account Balances by BANK_ID and ACCOUNT_ID", + """Get the Balances for the Account specified by BANK_ID and ACCOUNT_ID.""", + EmptyBody, + accountBalanceV400, + List( + $UserNotLoggedIn, + $BankNotFound, + $BankAccountNotFound, + $UserNoPermissionAccessView, + CannotFindAccountAccess, + UnknownError + ), + apiTagAccount :: apiTagPSD2AIS :: apiTagPsd2 :: Nil + ) + lazy val getBankAccountBalances : OBPEndpoint = { + case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: "views" :: ViewId(viewId) :: "balances" :: Nil JsonGet _ => { + cc => implicit val ec = EndpointContext(Some(cc)) + for { + (Full(u), callContext) <- SS.user + (accountBalances, callContext)<- NewStyle.function.getBankAccountBalances(BankIdAccountId(bankId, accountId), callContext) + } yield{ + (createAccountBalancesJson(accountBalances), HttpCode.`200`(callContext)) + } + } + } diff --git a/obp-api/src/test/scala/code/api/v4_0_0/AccountBalanceTest.scala b/obp-api/src/test/scala/code/api/v4_0_0/AccountBalanceTest.scala index 843335ec3..3e1e40d73 100644 --- a/obp-api/src/test/scala/code/api/v4_0_0/AccountBalanceTest.scala +++ b/obp-api/src/test/scala/code/api/v4_0_0/AccountBalanceTest.scala @@ -17,7 +17,7 @@ class AccountBalanceTest extends V400ServerSetup { */ object VersionOfApi extends Tag(ApiVersion.v4_0_0.toString) object ApiEndpoint1 extends Tag(nameOf(Implementations4_0_0.getBankAccountsBalances)) - object ApiEndpoint2 extends Tag(nameOf(Implementations4_0_0.getBankAccountBalances)) + object ApiEndpoint2 extends Tag(nameOf(Implementations4_0_0.getBankAccountBalancesForCurrentUser)) lazy val bankId = randomBankId lazy val bankAccount = randomPrivateAccountViaEndpoint(bankId) diff --git a/obp-api/src/test/scala/code/api/v5_1_0/AccountBalanceTest.scala b/obp-api/src/test/scala/code/api/v5_1_0/AccountBalanceTest.scala new file mode 100644 index 000000000..90b87513f --- /dev/null +++ b/obp-api/src/test/scala/code/api/v5_1_0/AccountBalanceTest.scala @@ -0,0 +1,32 @@ +package code.api.v5_1_0 + +import code.api.util.APIUtil.OAuth._ +import code.api.v5_1_0.OBPAPI5_1_0.Implementations5_1_0 +import com.github.dwickern.macros.NameOf.nameOf +import com.openbankproject.commons.util.ApiVersion +import org.scalatest.Tag + +class AccountBalanceTest extends V510ServerSetup { + /** + * Test tags + * Example: To run tests with tag "getPermissions": + * mvn test -D tagsToInclude + * + * This is made possible by the scalatest maven plugin + */ + object VersionOfApi extends Tag(ApiVersion.v5_1_0.toString) + object ApiEndpoint1 extends Tag(nameOf(Implementations5_1_0.getBankAccountBalances)) + + lazy val bankId = randomBankId + lazy val bankAccount = randomPrivateAccountViaEndpoint(bankId) + + feature(s"test $ApiEndpoint1 version $VersionOfApi - Authorized access") { + scenario("We will call the endpoint with user credentials", VersionOfApi, ApiEndpoint1) { + val requestGetAccountBalances = (v5_1_0_Request / "banks" / bankAccount.bank_id / "accounts" / bankAccount.id / "views"/ "VIEW_ID" / "balances").GET <@ (user1) + val responseGetAccountBalances = makeGetRequest(requestGetAccountBalances) + Then("We should get a 403") + org.scalameta.logger.elem(responseGetAccountBalances) + responseGetAccountBalances.code should equal(403) + } + } +} From 74d5a64691a95702f0d83c3f5cd0af10ab162d16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Fri, 17 May 2024 12:06:10 +0200 Subject: [PATCH 11/75] feature/Tweak endpoint getBankAccountBalances v5.1.0 --- .../scala/code/api/v5_1_0/APIMethods510.scala | 15 +++++++---- .../code/api/v5_1_0/AccountBalanceTest.scala | 25 ++++++++++++++++--- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala index fec1deec9..837434a50 100644 --- a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala +++ b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala @@ -2160,16 +2160,15 @@ trait APIMethods510 { nameOf(getBankAccountBalances), "GET", "/banks/BANK_ID/accounts/ACCOUNT_ID/views/VIEW_ID/balances", - "Get Account Balances by BANK_ID and ACCOUNT_ID", - """Get the Balances for the Account specified by BANK_ID and ACCOUNT_ID.""", + "Get Account Balances by BANK_ID and ACCOUNT_ID through the VIEW_ID", + """Get the Balances for the Account specified by BANK_ID and ACCOUNT_ID through the VIEW_ID.""", EmptyBody, accountBalanceV400, List( $UserNotLoggedIn, $BankNotFound, $BankAccountNotFound, - $UserNoPermissionAccessView, - CannotFindAccountAccess, + UserNoPermissionAccessView, UnknownError ), apiTagAccount :: apiTagPSD2AIS :: apiTagPsd2 :: Nil @@ -2180,7 +2179,13 @@ trait APIMethods510 { cc => implicit val ec = EndpointContext(Some(cc)) for { (Full(u), callContext) <- SS.user - (accountBalances, callContext)<- NewStyle.function.getBankAccountBalances(BankIdAccountId(bankId, accountId), callContext) + bankIdAccountId = BankIdAccountId(bankId, accountId) + view <- NewStyle.function.checkViewAccessAndReturnView(viewId, bankIdAccountId, Full(u), callContext) + failMsg = ViewDoesNotPermitAccess + " You need the permission canSeeBankAccountBalance." + _ <- Helper.booleanToFuture(failMsg, 403, cc = callContext) { + view.canSeeBankAccountBalance + } + (accountBalances, callContext) <- NewStyle.function.getBankAccountBalances(bankIdAccountId, callContext) } yield{ (createAccountBalancesJson(accountBalances), HttpCode.`200`(callContext)) } diff --git a/obp-api/src/test/scala/code/api/v5_1_0/AccountBalanceTest.scala b/obp-api/src/test/scala/code/api/v5_1_0/AccountBalanceTest.scala index 90b87513f..fd342c5d7 100644 --- a/obp-api/src/test/scala/code/api/v5_1_0/AccountBalanceTest.scala +++ b/obp-api/src/test/scala/code/api/v5_1_0/AccountBalanceTest.scala @@ -1,8 +1,10 @@ package code.api.v5_1_0 import code.api.util.APIUtil.OAuth._ +import code.api.util.ErrorMessages.UserNotLoggedIn import code.api.v5_1_0.OBPAPI5_1_0.Implementations5_1_0 import com.github.dwickern.macros.NameOf.nameOf +import com.openbankproject.commons.model.ErrorMessage import com.openbankproject.commons.util.ApiVersion import org.scalatest.Tag @@ -19,14 +21,29 @@ class AccountBalanceTest extends V510ServerSetup { lazy val bankId = randomBankId lazy val bankAccount = randomPrivateAccountViaEndpoint(bankId) + def requestGetAccountBalances(viewId: String = "None") = (v5_1_0_Request / "banks" / bankAccount.bank_id / "accounts" / bankAccount.id / "views" / viewId / "balances").GET - feature(s"test $ApiEndpoint1 version $VersionOfApi - Authorized access") { + feature(s"test $ApiEndpoint1 version $VersionOfApi - Unauthorized access") { + scenario("We will call the endpoint without user credentials", ApiEndpoint1, VersionOfApi) { + When(s"We make a request $ApiEndpoint1") + val responseGetAccountBalances = makeGetRequest(requestGetAccountBalances()) + Then("We should get a 401") + responseGetAccountBalances.code should equal(401) + responseGetAccountBalances.body.extract[ErrorMessage].message should equal(UserNotLoggedIn) + } + } + feature(s"test $ApiEndpoint1 version $VersionOfApi - Authorized access, no proper view") { scenario("We will call the endpoint with user credentials", VersionOfApi, ApiEndpoint1) { - val requestGetAccountBalances = (v5_1_0_Request / "banks" / bankAccount.bank_id / "accounts" / bankAccount.id / "views"/ "VIEW_ID" / "balances").GET <@ (user1) - val responseGetAccountBalances = makeGetRequest(requestGetAccountBalances) + val responseGetAccountBalances = makeGetRequest(requestGetAccountBalances() <@ user1) Then("We should get a 403") - org.scalameta.logger.elem(responseGetAccountBalances) responseGetAccountBalances.code should equal(403) } } + feature(s"test $ApiEndpoint1 version $VersionOfApi - Authorized access with proper view") { + scenario("We will call the endpoint with user credentials", VersionOfApi, ApiEndpoint1) { + val responseGetAccountBalances = makeGetRequest(requestGetAccountBalances("owner") <@ user1) + Then("We should get a 200") + responseGetAccountBalances.code should equal(200) + } + } } From 773ff2b4c830e6ebc7ea365a0a34569791b6b9c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Fri, 17 May 2024 13:27:25 +0200 Subject: [PATCH 12/75] test/Rename getBankAccountBalances v4.0.0 to getBankAccountBalancesForCurrentUser --- .../src/test/resources/frozen_type_meta_data | Bin 141210 -> 141224 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/obp-api/src/test/resources/frozen_type_meta_data b/obp-api/src/test/resources/frozen_type_meta_data index dd4635a28227953298bc877a7a9b0f5c296202d7..f3fd89aed6cc4dc105134eb648f32773e4915f6d 100644 GIT binary patch delta 2335 zcmZWrdu&r>6#s6!N9ovumTm3&-r7B(d%zyd0c!_C1`7xR2?q48z3a-_-tPA69z;P! zk|hGc9gtv<7-Gy~mH;+J4MYVKqeNLmNE8)BF(U*8B_xuV%c@c2sg$<8R_u`?aJc?3*gQ6i6 zip1W*{2os)b-dxY1}@p08nPK~v^l_NZx>APwA~B+2+h35-?NW)6^D#057iyXH`m6_8`S;SemU zT2n-ck-6EFkQ85%_A#4O8m0-@Q?+2sRcOSxSRYo#XqZN%D7piq%cEsJp}w`y%$yf8 zNVk+oh#_f(97t>&5>Pq;;V1e4|XN{B;sKL%k45~xvF#eap zboGt`RtU8bGlh)Rw9lg?PZf3~6I6~ehp8G@;eXI{pE2oYQrBwE3AvQ$17akSSR0Op zDO8tvpsmr_K|W6)X6{f1A*7ZKJC-~k$0aT{pl*J;JF+q}JvdRQDMn&~(Z(mt#EQI) zlKgZr{Ucb?)j^nSEP_)x4qpE9Sk|;&Lt5crQweTbfQ1O#JZ3oLap$AGj-;ZnmSH6DQPWZl zSq^_U1=*&}Rcq&0#8{wDwZ}od%}ra?&B90KDNFT)(X3Tl?F))=$})@f%IT$GRa4Ee zkkkUqZ19s(gsr$v3Hl4t=~^etZXJ-Vk|Ml;uJHDhCbFP zDHf7Ye*np4(6x4I$Zy~*x4g$(3R4u0Aps~X-NL(i40f0H z=8U6o0?A3ZSK8`A?z3SYCE+wn!^%0h*jwkVL!BND3Pn1>tmXlK2apJabHsrl%<>2}wsTQg$xHtmYh) zvDIKQ3Kz}A@NL#kZsr2?Wc%Q3_UT$gV4WBO(@*!$FGkpWNO;?4O%f$ecrG73&V{2o zAGef*TRI!(22=Jq%LLATor>xG0*`1C9h9Z6=Deh;zDdcst5o(u>T2F;0h1AQlvtr4 z#{{W@Hg4f^>S2Lbg*8H(VGkF+p8C$PRm+n)!A?JG8tl$1N(EUlxqcj;FaDWx6A(3p zI5!2?O^Z2qD`ho*p#pEG?v)rdTzUucY&$r29`@S0;bps1qsfG~?WMaha`g`IA|ZJN zgB?Y}!#;$v<@k68xnGftvpe@_0Xv5`u=PdxNhG(~PN=iiIjk=Jtoz9T!u>~c13T?{ zxZms$^1#@#$P~i&ac_iXB<=X9P*TdvHrSG9VqMH<`_>^U!I^I|VjjS;7E3l?CgA{l z+)|oNuAuyTm}+USA~%rdfxnB~gn9!u$Px7pPNDD?B~|R^m%xrC1}!-=3u^_R5+r6Ru0n3&Jw`L^3z7$FPnxL)*$v#E|zKI9mio2Cp zd@(9o*-+qN!+(fkZn_P{SCRan9>^mDCXKO+{K)Q_YJDRWX_7D2P5ckw9=oMrSj3lD la(JX?1;LTLGY=U+xC8Cw7Og@Sz=UAglWhGU*(!K`{}%;z_XPj| delta 2404 zcmZWrdu&rx7(eIk(QWP4j?tViqz>5}GM+|(yY{Rr>w3$*r?43! zD)Ao$#eA5ke`rj6#faI`_>K}1p9xBc#Kb=!Na7#Hh`~goF$TZy+#(bI`Eu^}I^W~> zedoS@uH^Z1CG(GQ=h{$bs8g~6VU{@T1MtJ#iQ9&3a-{) zs*uci)5L=F4R4o8=0H|NPfx=WjoaX~7A&kq1}rrCfCnF}mTU=r4jJZRhwyMP?DOXB zZd|0N^mI&Ea5|WCNLFYXhJ+WEP3A8hGSZevOAAVy>T4wPW;1aNG9P_dN;5f;l_IyQ z>2{6E#h#Bw1ZT!2a&K(vtk$R_=r5M>t$w>di$~Y~(QSYt9{O zUEoxlm?`iEmD#C=06fsPE}v{z&RDq@+Cm&M#Xk6^{Sk`C*8X2SyI0*;0B1e^LLVx? zmR4`>La2m$BrE@q0ervua;0PyHghOzNn=Ks@L6ZVDH#PzX;`qPYi+4y#rTaeP_}^&!>?r_ zaP=ZH0HNL{nAQSCekw2owWZAm*h6d>Z;cu0$z+1Q9w%~w$g`69>3UL}AjTQVTy{JW z6WcjOybF?fFyx`EX<`SXvs85NeLC&Zmb%p!&B5caR} z^LF*&I#kFQYehb9Bw;t4@V6k`%F%9o&XQtgM^YRj$uOMuxB4Zkww+Tc!&y0nh>~Jf z!cTsHrz`xGp14Q}DQ?=rUllD*a&SVjmFN`~YO5euGXnRz+n~61B^+}%!(i?9D$LJ@ z<09LWwUh1(WPA|`Z;`A%3kMGSp`oT2S_6JqsA_?O{aErbc((tpQ3Xx8MVK>5%Ss~F zqlQV)TU4Gt^0uA`cEg*=PNb5tyx0n-QlyRJ-b8_ZUEBzN_iv3J!7?HiHqXVdgxMLa zzGS{zEag~Qf!rQcbhI%olQJbpTNU~^1l<8RG|-Q8<S#~(qFg9Cs;nR88~jD2^9S5L7yys6Q{xgM&Z#nD28h(ddFP!cn^1EqD z#HJ?9p~apSDkV(LN4wBSNU_~f_K7486dI`0f@;jg7o+yJ&_P53D-+jiNc5;A2AZiv|SAn+(Bdmyyb3J z!M_IA+`77FBM47I@jizup8e`{0q(EdpiURzROK=c*|rct@Yh)SL6~p%=B`)1!dDQp zg2>3M>M>KaSi>OvVSgA?3a!aq?m7hL7hb~4$4iP&%-q6b2l8Qqj~+szaS z8xiPgj(}@^svQrKt3J4>b*RXO!BY^W6W<9&L9;sTg7*scs?$?2SlC@k7L$Zj*z54a z$-xZ<4GQ5d_Dzn%p(V5e>er3U+9A zp7!ckO-`1QE}_AWlg5jmCBZNZEWParKSzvR#Mmua6+SuoPTdq!M%EJJLO1ONCrFL~ z(~GufdbWxKS$u(2`Ec+E`r%CSu9SgG&7330-=R<9CK9Gd;8q+J_>S4T?j(*z7DCk{ z8W(W5y4Fqlv`%@F$Ta*|yC0S0~8FvlV3&-^g_G(jl z(#WqC(m)|Sl{ePNut-s9c`1C_+s$j?&))9pdeZA45+b6od=$ccZG*Um+*slv-HhK@ zSEPYUC% Date: Mon, 20 May 2024 11:22:58 +0200 Subject: [PATCH 13/75] feature/Add getBankAccountsBalances v5.1.0 --- .../main/scala/code/api/util/NewStyle.scala | 10 +++++ .../scala/code/api/v4_0_0/APIMethods400.scala | 6 +-- .../scala/code/api/v5_1_0/APIMethods510.scala | 38 +++++++++++++++++- .../src/test/resources/frozen_type_meta_data | Bin 141224 -> 141259 bytes .../code/api/v4_0_0/AccountBalanceTest.scala | 2 +- .../code/api/v5_1_0/AccountBalanceTest.scala | 22 ++++++++++ 6 files changed, 73 insertions(+), 5 deletions(-) diff --git a/obp-api/src/main/scala/code/api/util/NewStyle.scala b/obp-api/src/main/scala/code/api/util/NewStyle.scala index a8b459b18..bbd0ae290 100644 --- a/obp-api/src/main/scala/code/api/util/NewStyle.scala +++ b/obp-api/src/main/scala/code/api/util/NewStyle.scala @@ -372,6 +372,16 @@ object NewStyle extends MdcLoggable{ } } } + def getAccountListThroughView(user : User, viewId: ViewId, callContext: Option[CallContext]): OBPReturnType[List[BankIdAccountId]] = { + val viewIds = List(viewId) + Views.views.vend.getPrivateBankAccountsFuture(user, viewIds) map { i => + if(i.isEmpty) { + (unboxFullOrFail(Empty, callContext, NoViewReadAccountsBerlinGroup , 403), callContext) + } else { + (i, callContext ) + } + } + } def getBankAccountsBalances(bankIdAccountIds: List[BankIdAccountId], callContext: Option[CallContext]): OBPReturnType[AccountsBalances] = { Connector.connector.vend.getBankAccountsBalances(bankIdAccountIds: List[BankIdAccountId], callContext: Option[CallContext]) map { i => diff --git a/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala b/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala index aa8631ae8..98d10cf31 100644 --- a/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala +++ b/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala @@ -3346,9 +3346,9 @@ trait APIMethods400 extends MdcLoggable { } staticResourceDocs += ResourceDoc( - getBankAccountsBalances, + getBankAccountsBalancesForCurrentUser, implementedInApiVersion, - nameOf(getBankAccountsBalances), + nameOf(getBankAccountsBalancesForCurrentUser), "GET", "/banks/BANK_ID/balances", "Get Accounts Balances", @@ -3359,7 +3359,7 @@ trait APIMethods400 extends MdcLoggable { apiTagAccount :: apiTagPSD2AIS :: apiTagPsd2 :: Nil ) - lazy val getBankAccountsBalances : OBPEndpoint = { + lazy val getBankAccountsBalancesForCurrentUser : OBPEndpoint = { case "banks" :: BankId(bankId) :: "balances" :: Nil JsonGet _ => { cc => implicit val ec = EndpointContext(Some(cc)) for { diff --git a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala index 837434a50..5ff01f51c 100644 --- a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala +++ b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala @@ -19,7 +19,7 @@ import code.api.v3_0_0.JSONFactory300 import code.api.v3_0_0.JSONFactory300.createAggregateMetricJson import code.api.v3_1_0.ConsentJsonV310 import code.api.v3_1_0.JSONFactory310.createBadLoginStatusJson -import code.api.v4_0_0.JSONFactory400.createAccountBalancesJson +import code.api.v4_0_0.JSONFactory400.{createAccountBalancesJson, createBalancesJson} import code.api.v4_0_0.{JSONFactory400, PostAccountAccessJsonV400, PostApiCollectionJson400, RevokedJsonV400} import code.api.v5_1_0.JSONFactory510.{createRegulatedEntitiesJson, createRegulatedEntityJson} import code.atmattribute.AtmAttribute @@ -2192,6 +2192,42 @@ trait APIMethods510 { } } + staticResourceDocs += ResourceDoc( + getBankAccountsBalances, + implementedInApiVersion, + nameOf(getBankAccountsBalances), + "GET", + "/banks/BANK_ID/balances", + "Get Account Balances by BANK_ID", + """Get the Balances for the Account specified by BANK_ID.""", + EmptyBody, + accountBalancesV400Json, + List( + $UserNotLoggedIn, + $BankNotFound, + UnknownError + ), + apiTagAccount :: apiTagPSD2AIS :: apiTagPsd2 :: Nil + ) + + lazy val getBankAccountsBalances : OBPEndpoint = { + case "banks" :: BankId(bankId) :: "balances" :: Nil JsonGet _ => { + cc => implicit val ec = EndpointContext(Some(cc)) + for { + (Full(u), callContext) <- SS.user + availablePrivateAccounts <- Views.views.vend.getPrivateBankAccountsFuture(u, bankId) // Get all accounts at the bank the user can see + (views, _) = Views.views.vend.privateViewsUserCanAccessAtBank(u, bankId) // Get all views at the bank the user can access + canSeeBankAccountBalanceViews = views.filter(_.canSeeBankAccountBalance) // Filter views which can read the balance + allowedAccounts = availablePrivateAccounts.intersect(canSeeBankAccountBalanceViews.map( i => + BankIdAccountId(i.bankId, i.accountId)) + ) // Filter out accounts the user has not permission to see balances + (accountsBalances, callContext) <- NewStyle.function.getBankAccountsBalances(allowedAccounts, callContext) + } yield { + (createBalancesJson(accountsBalances), HttpCode.`200`(callContext)) + } + } + } + } diff --git a/obp-api/src/test/resources/frozen_type_meta_data b/obp-api/src/test/resources/frozen_type_meta_data index f3fd89aed6cc4dc105134eb648f32773e4915f6d..3f67994246f72756bb6dbc9ecb50d66836d0d0fd 100644 GIT binary patch delta 8087 zcmZ`;X<$v)7S1{{-^_DDCP@rIP!bh_pjs01)RM^LlE|D~QyUUZOVAjy$--+Zv|a@j zq+HZkwA7TeTJ=IhtHo;tjjB2C+vnb^dDZ?o=bm%+UVH7e*ZS7C*4=p4`s7*bZ4X;j zH??xIq7d=1#?xU?cHV&Ld3o9_-Pn9>o=6C8DXOY1tHXkuoXJvzMMSzU%r%=!*7C9v z7qhQ77 zn%Y!5^|nwbPI&ZLNa58LeG(KZDT_{}5|I|2f$O)?(=k4_|9*V69q>8fRy#0X-d-AH zi<=nnvY-Nv&LvOr=;a{1_kTs(0>U4zpmcFNrUr|Vp4_SzMF~w&5797T zAf$Agc#NEjb!OH%dD%JIJl$-=+BC*Z_TL=V74NjEo}A<+?IpEYzcoU9 zksRXKpfj@^t;?J;ir-U8p6Twu@9!aJv2$|0rbK7vnyJl9*5)Pf z_Fm-4p&dvghThhQQ(8ADb48o4*kF)_HZv(LLrl8fO3Y4mg)K@`2jO}ybq-{YO3Q%k zgK6iefjgDqAsdF2E}sIyBdvb!Ha{*TYQ#257Gu^ z%*75b=8zXBPNBZSC$kcBk7f>g-mZMdkNm~T>(1ixb#u#nDK=chXO%*O@3qulbjpr| zsLPIuKikcsV6izn25!~$8ys2poVJh}mopMNZ_Vilb?b6Y8r5;-nguj^M_vv*Mda!( zVi()5QlQA5-Vra}oqk2#OIy*jL?gD%2*KX39QGEsW~5=UNnI$hde6*`EpVF7@A>l8 zZZuqsdLz;}=$A(?iC^A$0o(K|&_Ql5C-N2B3i?BCW5Fz#I%7^ttjaedsWUg&DA9cA zEV**0`P75g9;8T7P!s~^Utcr_It0v1z>K1Kw+Yg7iwhv6q4>mK9e@Mv=_R3gyCW9e zw!%{ke>()froWwN+^vgox9^bnbOAy{}*1&M1c`G6ByiIlP`!z_d~hk0(wClEe*9t1UH-gsC0sr^uw%domh9;Rb(%D0b5_cq!f1OvUD!N zG*!#qhH1i=XJeBamdBW!?cVYjyzXBnBlx>A2|_Q4EX?{u_$m1C^~!iBC~B2IJu@>Y z&k(gM=g|nUtUL%pKPkTtZNonD5sODvikww%;ayF=htRBcz&ZP@eif4PSFc7WR@?t; zKA?~HXHa{w|Gf#A=JkG^U9t2{M5eh;JF9GrJJ+2;fcdWt!qNlQPQ)PEZaPk^Ti@NXP+dmUtzQaF(m%){b6CrT zoko(IX#5b$0Ql;qmSG=tl*XF4@gOv4St*URxbj6@k5*>jP1jAI;(BvaGTw~XoQ~_h z&Ex(rl*Vo8X9}frTjWIhk4r7pT^5LnkKe)UPFv$t#U(ODVoR5{Mwl?YLsbxtZFto= zT!Xhg!Zl#~63D39o{MYXjs#ri?U-X+Q213>nAUn+-5zSU`Q$7Xxc5mWJn&`=`SH=m z8(705E|Cx4c2V5L z#ofU$3+=fety8x*Oge?_{sl%cRvd7iV)>~(eJA!Ec*W>c{4^NY7H}{Gg6sB9sIEBp zolGIK4^PF8t{k4G0e&lKlU20iNKYCiHh$Iy1I~W-8fLUVQjRqa9T|oVIUYT4T*;E1 z9cZc;bL?Nxq7q(=wB$ zYV&nwp`x}XK#Zuh6N^rCg`9mSIzTbbWb)^B9guh8PTKMPAIVNpN<{aQ>CmI%guo?8x?r<_U{37Hoq>w`{ln*H!5Q*M$QVT?sOB3MDDl@AI~*RgH!drm24{yIwe(` zlAbT>%e+MW?~dZDTLU3oCZR63dqZ~SZBm|hzLUSTGi{aw-y$F3cy*%4Zd?hu7I$-D zw3&AY(hOdA8!73+U2iH7jdus(b>#1RaJ~Dx7RpSxHw5mw;a(-Kxv~Bt>yJHftfWP7 z!l?UUP$J`gEEL#tAMj$JB~Kr0f~3laDcFSlqe5K!CAzUqh$3?*688=($wRHjWUJ}h zkV=1fH1FAdTsYoZ(Qw^05QL&98rvbGhYwSS#);a;O(w|s_Q@0!ZuIo0E-n@NO2A3U2{D8_#Q}&}be}PIjVjVNc<|(wZ~oQ!blZ5eTim zofRP_i+PMStw6fCVNL&1L8A|{BdjvkitB7hV+oajAO@h>Hp5cXq?q$19XYU%E!i7S z)3Uki#^X4S+tL_l;j5uWtmJG*js#CbL@mPO7JGVAdZavssQqNmBOJ+t3VEs{N&Zsn zNCzNdnG?;%Eie@Z@O6f&63;_9_8Mt;t}`Kb>)&;z7>O#^T?lYyU|C)4$bzT1(sCnS zbGRFUAMu?~#MB$BFnO&TnY_@F6TQJM7LQ8he&xXLe$&X!!jQmniafd0oqQnopO_H8 z>p`7iz*8QS@>gHa$NR0GlrN9`WtFE3hpTb?u@`~q>sxx$hwx7QAs)9TTMq%m5?@B zdQeibHbdP8U-5;>OE|-ik|67hA0Z>^JNVN$V{q`A0QwFCCisvqFAJnmGPtdiQc|*~ zXL0Usaw~65Ltz8dRFB~#F+n6Cr#^^Yk!OPfg5$I}!G#gzZD(M=YRzITZubh{(qQ@u za}z`8SHrEPcz;whSGFM(Ytlv4On4NM#I6r z@aa%^u6mX!e^Z(8ln#_*^hkGP-7%u#V3auh2>~y04Ab0NF z3fyhKmI4gb9>7ZmyB;-T2DvmZ8w`~*C+Sc#|N9+h$K>SMgEMoo^K@#+s36xJI~tdY z<0@o_G}?qGgpDCy+^5k|6W^I#$)H$#0r{V0 zRMxlu{RJj1GGI8T`gye{E+GvQZ& z?U_IkHR<%`4`xx8(Hzlw_~dB|{iTNuxc*;2eAIzlKoc z8Ky5;BcX=(t)ctIt&EmPv!62uk&S-saC&U1pYs7tH8+LLe?OxB&$j`1=tebWq!mac$=qY=j+(N1GHH6J$X4usA++M z%|e%^%Ya!Yc#8s<72oW5YZZkX4Qa=@Z<3q-X%+R6rg>zGJS@>a;5 z4m?9YjTkwhz5bg6z&3ohJ47>#8}(?;5S&OK`Wdy9Y79C;*Re?FqmYXkqmNOt^hfiI zw0xb;tUb>Q1oFRo4AFY|7!CXzm{*xurzSV9a#CzK=X0_!u4t>)fMQ8jOWyW5NQt4R z$CY1Dn9)zmmV5BYc4S|EoSYq{@$yk-8Je$Z--^2*rfn)dEeBiJuXl(68ugP|K%Z zA*q-SVE8#i@}K<8fsdbqzo}hXRJ{D|H}pC_BORx5;kQ&`JWUOcJc9;X)&6ik3+}BrVjG&{0H`wjo=_&Qio$e@~$%x z4qrpJ1LrE%B-;NC=M{SrdvW6n6y)3=UNCsF!S>QY3b^Vr4Ep#Yp-s+Cmq2+`+Nm{c z)kE#Q^QB9KekA9AkF;vQdgpqCUI|x7(mk<}g4wz|2>#kovM?Tm$hp;uv$gQ2L-p{d za6Vg4Fbn_tvO4j?WffB$uYiHZ$+F9j-@GExt^5a-y-ofBbS#{a6$144evpM>F?VdC zc(LDAIA6G!`*ltA>*v29Zupt}VGM_Bs0m7V`ZZNi&2<{+0OrxW#)-dHY4@eBR;A3|7G9L{_w7%^P@E4e|1Rli~b zaa^k?-t2x;4FZJHWTQV=h&gxr7@8L?)f%!uOnT3ga~cRm7cXs~wGh$y7N!+)esozmI}Twp)V?&n+HMcN2D@po?i0|Vi3Cg28#TCA4j#=qY9vlfa3+%@KkslDhd@n7H1 z0`IZB`VUPHE5!{u6s@3WF11pIsA*KNgQIK|*<~HE*;kysZYeJJcI0v!r5$X3&_>xn|Hp5l_lEHJI`HrH8U=s5 zso1!9lveovI}dGs2fkpZ40ibQAGKLJvmRpJ-e4YMuXtgnnf8hdr+Ry32UIcS8 zT@5Z10bxDd6tqzJLpNojp>xE2+?6PGxR1Wv9s0qV0oLfSENbvHk;5N7ln-R>;Kfdl zkV)5iD$UCVuJKel;#8k_D&;WO3NOX`?@B3|WBH=DBLCid=&khrrx*rv)^LuG5`b~b zeUv2Qae3nGee|v^l}ylMp63fkD&a$6sOC&TZ_O@#3W9;#`6>ChscnI5OFh5nbz!xT)4P3j9J1vTdOOy4^lG! zX&X{3*90l8jrv>4g&9mUlGB56%Ef#sSdmOgRdalZ0=lX%43TFATx_Gr^!B8UviX1Y CWg1`r delta 8094 zcmaJ`d0VeuO|R7o z6+`M>>Y0j`QbfIa#zO?Ac1OE7x1tOq`rqm|RqlAsk>UGm`zEFh=|L_a=&GiDO}@AAl}mWS>Wb9U>L4>10?5| zAKgT|V>C^tsTAGEEE~yaSK5Z>K4^<*)BW{LEDerW@k)JK1;= zGt8AhWa#jzR3=UgH<;wmiki4|aeKrq_v49{) z&w4+O4<}oG@*U|!QgSoADoyT3E(v-I%be8gbV=cckxXKmClB0AHoSilF7Zj^B0N)D zKtn0BDOenw-=elW)eQ=UoL=H^O@#PmOmoK@dJD}EeL+Ufcq5TT_dGe$K=z!{jI^R( znmyJJPuow$e7upoMaQwZkYL?d52=@(>2W5FIEiiFxz_rP8>Qeeev0|moW%zdT=8$~ zgg0@$KVdo*i=vDyko!L44Bg;CmE_4zr${5tO!UNx>l24So$DsG6irKQ`8-4r#hFf; za-&#FGdr4CS#eDW1o<(u6r09p72|x*^2kd(&ANvDSF;EHxq`WPGU6pC#~A{Y%>ugK{H5VDA|Ub5bWV3F5~t>bLN_tN*y}#m1COO2J863 z-WeMS3TZ`!`dohN33*lavlBTLvDmY<0-EH~ZWJIMS0vjb7@oWFG|>?6!ApHMm!*7*SCvj|;5leEh;mDFWv&(r`#^G^#rHt=Kl@d%Q zh#c%ZEBqC_xVJ3P3DRo|i?Xv*3yhJ>f&SEc+0Ufo0i&VQJyqUdaklCK&OP`OAK|;V zns3@E4r1N=lkmc6sD}t!?f~cLv3vvwS1n&IQLw|xzbTZ!SF)(JxUy;#_I6)=K~p9* zaYOj7=?C89)+CtpV8dI_QMAymoe44(YooFH*xLKJO50pmH|P~lP|Y{^?;h&e7i)@* zWS{;v2nwgS<>Q(+z>oJ|1!lG1t`RxgJLAzu z+ov0O2Z-3(wPv;fpS8wW`h9j1yu5d0LuRLV@)zrOq?&Z&ml9n5PN5Xz7_0-*D;Z-|8017xdRrX( zqJ<9OWu2X#t_N;&Lp-^P#aSBe767$}9Bc`S@dv9QjuB1mh36q39J$k>vyjyGu(cG} z^~-q@t5+ZC0R?Q6==R%@=9X}1SKi+nvC`q_8`fZ=$;eI}^T&c#OuVqg8yw$euy zGXl8HF@T8}ek@Zb1EHuey&ySbtjy6I5I{Df@mPDj4mci%ZJEc@EO9$|N+u4z{e)ck zqQBxSY)|``6sF^aT8gdBIvp;>Tleh+AYbkD@0^<8w(wjMQ5oM#0*bY~5QvlYy&wZ} z$At=5BRxX6JVr+Dc8CBIHUCR$hE? zP6RXn)@!pHekHRxd9W=YFd^TCNB#n3Jp7NR$+dXGJ?bfPe<%jgn?J-uw(dWsSnkkU zgo^{&Zld9Al2F_9r$n%r`ctwr`MIm}uyO3QTw95oV{_Ar zveS8yjnY)?y`~dQewNY<{&|CfNT}P=S+u_{>zwIzk>cj{?s(PaMmJ-z&_*o0IRUTk z-<*i>7}GdL;;<%dLV8*zFR@X)#g;~$xZT(n%2e~z)-oH_Ry?3B%8R^PvPfEdi1V#VDzpsM=2GHyM7e-lp|+iLm{^PhrBgVO(F^6?&D`>p!ws;6c8ueLN4|6+7>=lQXg zE@k)Do4I-ShC*Yb%T7pEVGxdah2i3IU+^a~9MOsL1Ozehz&J-uv=v--}DV`ve z70-30B__P)5pE=nUF=4PV~*-dvpLV5TA_lw9ElQgt2_DNxvY`+p*!7$-Wok9?PU)x z#Pd2&D#TI==bY$8S8&dr-t-Yv&YFAF-QeF8WKE~hh{ey^@r5!JD~o(-0Jz=ShqSnA zp_U){5+I0u{pej6WBx?tk(N7Ft;MH_pvitHVeNTE++(;>PA~~;bIm1dHTDakP-El4 zDIwI_bb4zMR1nBpL+CX2=Cy$1uWn9tkOaTmNPWRyWlD?{$3J!zVI^AOv>=Gvx1zof zZAvQ|fm>ZGRM^0o47f>rYf7gC!}iwXF5xan@5l46!xwI~A>?#v+intq+Wbjd!*knG z9ymX2OF7DO_w5)J${&Q$RWO)^ECU*L9Y~L5sJSP+P;X0nPYb8%u-_lyl#PGpT0h?C zUzw0}Km^6&wkU#2G$= zm9=d~0?*$^*&H~U24PqJXp+aiG@6RQWk{k5FO7-x3I2uJP!R7-!kN{4E5UcwabM0$ zR^1573s3{}Vlpi<2WWK)b&w$+-qs$XbR3)stSRQhnZ&Xf2ozn~d8Jp}wW z<(3eL)Vv6&4L2&HvABI#MCjbP%_OKhL1u*_oaEjl%6K`g$`%~O5Pvio?gzx4LQ_qd zhYwAmJd?Y|a@{~$W;ivKKD5RveHm>4^}yrg%_kfb7w$U~$+4XEGZD?@{QXRNL-iaF z?lFtHyrQ9w2i%xvQU6yn_3c@NZq5K8+baV)pq#$J-y7w$6RkAEe7*Sb9eC5-xrlhc z%RDSsD{5J|>Ui@!B!O~1F^>wcEw+L}WYaco0R_LTUL*q@?_WT^IL4&~G!x5H7t$tK zeLJ0OCEL?Z;16K=QZ@^1d1NIHSj@94;R^};v{FI=dc{^!eQL*Uu9(fuc5`mMl=ew4 zYs;l$D1ejplbtEU%IVlakZ)Kkn`4ZD+NMG?j;;x)ezpt`_j^E+6%T-{x zxSY^GbC(ss@N!B-78>p35hI3ACj))!PR#lB+;W@6pQ;qyz&L^v|@@X}N%fq#n^)&b2Napif ziy1$4<)I-;BwyM{Ut{-%O){yg^B`XLDMh~Q4*bifDzG)%jEwMd32Z7XHTW(7f?cao0dCY5iqayTTIn|ibGU^0uwCgfTu(~Zc zY`8(Y70D31QnpdnOQXlRoU$FrS~MNZIoOf9*B#-v&*l{6lFhAfIP(GE() z?Vmd+1uy;nL0!?D%hcY3g@MN7)guFZs*J;SqI!UpcghmeaCaw7kx3?dHwsWZ?${em zSjrv>{@+=R2l9!g^553Hbq}F|=U?`KX)zDlOH)j?*NJU2S{okh1=Qnx`~5V@^upsG z?Pwht27EzHr9>GA>6+;cAbu0|vDF#LZw{vm6{Fmi-P$0R}^M)$*Ln1?g&e1 z3egN1r>4_nsn5fYP*Y8UT8{CQBQ#w$y^dp2b27Oh1(S@xqbk&89i@qgSKqo&bl=Xr zbuUUT->=C5;vxm7L92_tmS!mDBgbes&W>2B#I5!?Fb*Z~H#E{58}q-RXiG5hlBn#? z`|RK=zkNd(L9y=%lEcQ593N4!-rcb31TB`qs*X1+`q*(`4Jyjfr(l-<^++c^c#5LT z44l0{)~60Ru#CIasp8MBLm^Sd*Xn50i$2H$PSdw2%Ywe8_EPr2XVelKNq(6rw~Hak zF8txYV9#RS_b+-E(wM8k0U~9hmaD&mYld^ueM~tZGyl^?$ufL-769*vGrAV0>-AaC zcaA=pQ+Hq(o^p=*8N(_}+*;$wjpx+by2p9yFO}l|JXPtz(d`1XCr7ZDUdIFqr*|!R zpMk8*DfY?*lo!UlpLmhl{z>g9ULA?^zb-y^3bwffy_fTAmna&9Q1KbPG363j^3!Ot z<~5g~Y_(?e;KobzF&0-{mQ@n~`8AHNR~wwgmMFz%)l-Do0{iR9JgwHU`WBv93l8`i z!I!SUGUfdE3U!wKn^jRE2R(qrWG!F0#*&9M022~qN$k(-8&sSM{$A|~%D)HbgflQa z(BS6br=TW&Z`VVvs^@cj5Xxqo`sV2i!o27)w}VBLm#k^2HkzX^L(uvG#6H zb&}B3%G8^$@)Wt}G`##Kd4a%@S&^aFU#h-x7o-*xBc-)Wk8_G)UEY>aK} zwcp7PagWAvID*uG2*|(;hNBN7Wz|1`y46`|0$wHjfutidm@g-XlCSE%P0D!ZLz;|5 z9UjS>+HTxjq~Toy$&SxFqK+p0YV{U&qI96Ap~Yhq99ZAy2_-?!kDt&Xu*-Z(Q%$FJ zR>5$7!v}`7&**`4j^(5zLQVHcslY!J|6aUOQAXgf(7iX?H&J5o8!l9#cFU%ZV*Fzh z?CF#gjs4G|^t^ z1X&l`EBR*Q^8R!9(sF2@M`gUL*mqXLgB+Bx@|>npu`9@G9TnM#Om$S|ngw5R8=nJS zb5h>L^BO1R9qfQ5V)$1*f^ej>5&|)tX9e&oXJzaw^+UsdzoXc4vWpUm#rf;o8vuLb zUbMc3W84&Pa2VyLU{J|lxhV+{U!A#Ogd=)R)^V0QjwHAH7gW@To=W8&* zQ1F3;HJ-|cFyKZ{rNzrtrJMo<<6WSaQUxC~c!AxYM4!x>{LEYNgAjf`O4MI1QES@s zZOO%_Q0-4%>uBmv01vWfMaR{KQ(I#iGGjj~oGU&`XOkT~;Tt}Nh^9)mqPA!H)bUsx zqPAypHi1gWPm%p$UX%~}`YUoY7U!=N;-*3)IJY&CvX=#&;mrUg7v8H5jIIPq70Xs3 zfJ1|n@3G0LnKJPOE4%Q=%@kQS{JWV-Gd@_!{;NZRnvVS|kVKyZD_$n18gmu7iOQ;) nE3#mca;a7O?BVgPnUA*7Rp9&Khr|NRGR}^Dx3ZfrosS8 diff --git a/obp-api/src/test/scala/code/api/v4_0_0/AccountBalanceTest.scala b/obp-api/src/test/scala/code/api/v4_0_0/AccountBalanceTest.scala index 3e1e40d73..d11231ead 100644 --- a/obp-api/src/test/scala/code/api/v4_0_0/AccountBalanceTest.scala +++ b/obp-api/src/test/scala/code/api/v4_0_0/AccountBalanceTest.scala @@ -16,7 +16,7 @@ class AccountBalanceTest extends V400ServerSetup { * This is made possible by the scalatest maven plugin */ object VersionOfApi extends Tag(ApiVersion.v4_0_0.toString) - object ApiEndpoint1 extends Tag(nameOf(Implementations4_0_0.getBankAccountsBalances)) + object ApiEndpoint1 extends Tag(nameOf(Implementations4_0_0.getBankAccountsBalancesForCurrentUser)) object ApiEndpoint2 extends Tag(nameOf(Implementations4_0_0.getBankAccountBalancesForCurrentUser)) lazy val bankId = randomBankId diff --git a/obp-api/src/test/scala/code/api/v5_1_0/AccountBalanceTest.scala b/obp-api/src/test/scala/code/api/v5_1_0/AccountBalanceTest.scala index fd342c5d7..9e99cc527 100644 --- a/obp-api/src/test/scala/code/api/v5_1_0/AccountBalanceTest.scala +++ b/obp-api/src/test/scala/code/api/v5_1_0/AccountBalanceTest.scala @@ -18,10 +18,12 @@ class AccountBalanceTest extends V510ServerSetup { */ object VersionOfApi extends Tag(ApiVersion.v5_1_0.toString) object ApiEndpoint1 extends Tag(nameOf(Implementations5_1_0.getBankAccountBalances)) + object ApiEndpoint2 extends Tag(nameOf(Implementations5_1_0.getBankAccountsBalances)) lazy val bankId = randomBankId lazy val bankAccount = randomPrivateAccountViaEndpoint(bankId) def requestGetAccountBalances(viewId: String = "None") = (v5_1_0_Request / "banks" / bankAccount.bank_id / "accounts" / bankAccount.id / "views" / viewId / "balances").GET + def requestGetAccountсBalances(viewId: String = "None") = (v5_1_0_Request / "banks" / bankAccount.bank_id / "balances").GET feature(s"test $ApiEndpoint1 version $VersionOfApi - Unauthorized access") { scenario("We will call the endpoint without user credentials", ApiEndpoint1, VersionOfApi) { @@ -46,4 +48,24 @@ class AccountBalanceTest extends V510ServerSetup { responseGetAccountBalances.code should equal(200) } } + + + feature(s"test $ApiEndpoint2 version $VersionOfApi - Unauthorized access") { + scenario("We will call the endpoint without user credentials", ApiEndpoint1, VersionOfApi) { + When(s"We make a request $ApiEndpoint1") + val responseGetAccountBalances = makeGetRequest(requestGetAccountсBalances()) + Then("We should get a 401") + responseGetAccountBalances.code should equal(401) + responseGetAccountBalances.body.extract[ErrorMessage].message should equal(UserNotLoggedIn) + } + } + feature(s"test $ApiEndpoint2 version $VersionOfApi - Authorized access with proper view") { + scenario("We will call the endpoint with user credentials", VersionOfApi, ApiEndpoint1) { + val responseGetAccountBalances = makeGetRequest(requestGetAccountсBalances("owner") <@ user1) + Then("We should get a 200") + responseGetAccountBalances.code should equal(200) + } + } + + } From cb58afcd2df7765a2e2fe565eb17dd8ff4b0fc69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Tue, 21 May 2024 12:09:28 +0200 Subject: [PATCH 14/75] feature/Add helper functions at v5.1.0 server setup --- .../code/api/v5_1_0/V510ServerSetup.scala | 97 ++++++++++++++++++- 1 file changed, 92 insertions(+), 5 deletions(-) diff --git a/obp-api/src/test/scala/code/api/v5_1_0/V510ServerSetup.scala b/obp-api/src/test/scala/code/api/v5_1_0/V510ServerSetup.scala index 0d9344715..ec214b137 100644 --- a/obp-api/src/test/scala/code/api/v5_1_0/V510ServerSetup.scala +++ b/obp-api/src/test/scala/code/api/v5_1_0/V510ServerSetup.scala @@ -2,22 +2,26 @@ package code.api.v5_1_0 import code.api.Constant.SYSTEM_OWNER_VIEW_ID import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON +import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON.createViewJsonV300 import code.api.util.APIUtil.OAuth.{Consumer, Token, _} import code.api.util.ApiRole import code.api.util.ApiRole.CanCreateCustomer -import code.api.v1_2_1.{AccountJSON, AccountsJSON, ViewsJSONV121} -import code.api.v2_0_0.BasicAccountsJSON +import code.api.v1_2_1.{AccountJSON, AccountsJSON, PostTransactionCommentJSON, ViewsJSONV121} +import code.api.v1_4_0.JSONFactory1_4_0.TransactionRequestAccountJsonV140 +import code.api.v2_0_0.{BasicAccountsJSON, TransactionRequestBodyJsonV200} import code.api.v3_0_0.ViewJsonV300 -import code.api.v3_1_0.CustomerJsonV310 -import code.api.v4_0_0.{AtmJsonV400, BanksJson400} +import code.api.v3_1_0.{CreateAccountRequestJsonV310, CreateAccountResponseJsonV310, CustomerJsonV310} +import code.api.v4_0_0.{AtmJsonV400, BanksJson400, PostAccountAccessJsonV400, PostViewJsonV400, TransactionRequestWithChargeJSON400} import code.api.v5_0_0.PostCustomerJsonV500 import code.entitlement.Entitlement import code.setup.{APIResponse, DefaultUsers, ServerSetupWithTestData} -import com.openbankproject.commons.model.CreateViewJson +import com.openbankproject.commons.model.{AccountRoutingJsonV121, AmountOfMoneyJsonV121, CreateViewJson} import com.openbankproject.commons.util.ApiShortVersions import dispatch.Req import net.liftweb.json.Serialization.write +import net.liftweb.util.Helpers.randomString +import scala.util.Random import scala.util.Random.nextInt trait V510ServerSetup extends ServerSetupWithTestData with DefaultUsers { @@ -89,5 +93,88 @@ trait V510ServerSetup extends ServerSetupWithTestData with DefaultUsers { response.code should equal(201) response.body.extract[CustomerJsonV310] } + + def createTransactionRequestViaEndpoint(fromBankId: String, + fromAccountId: String, + fromCurrency: String, + fromViewId: String, + amount: String, + toBankId: String, + toAccountId: String, + consumerAndToken: Option[(Consumer, Token)]): TransactionRequestWithChargeJSON400 = { + val toAccountJson = TransactionRequestAccountJsonV140(toBankId, toAccountId) + val bodyValue = AmountOfMoneyJsonV121(fromCurrency, amount) + val description = "Just test it!" + val transactionRequestBody = TransactionRequestBodyJsonV200(toAccountJson, bodyValue, description) + val createTransReqRequest = (v5_1_0_Request / "banks" / fromBankId / "accounts" / fromAccountId / + fromViewId / "transaction-request-types" / "SANDBOX_TAN" / "transaction-requests").POST <@ (consumerAndToken) + + makePostRequest(createTransReqRequest, write(transactionRequestBody)).body.extract[TransactionRequestWithChargeJSON400] + } + def createAccountViaEndpoint(bankId : String, json: CreateAccountRequestJsonV310, consumerAndToken: Option[(Consumer, Token)]) = { + val entitlement = Entitlement.entitlement.vend.addEntitlement(bankId, resourceUser1.userId, ApiRole.canCreateAccount.toString) + And("We make a request v4.0.0") + val request400 = (v5_1_0_Request / "banks" / bankId / "accounts" ).POST <@(consumerAndToken) + val response400 = makePostRequest(request400, write(json)) + + + Then("We should get a 201") + response400.code should equal(201) + val account = response400.body.extract[CreateAccountResponseJsonV310] + account.account_id should not be empty + Entitlement.entitlement.vend.deleteEntitlement(entitlement) + account + } + def grantUserAccessToViewViaEndpoint(bankId: String, + accountId: String, + userId: String, + consumerAndToken: Option[(Consumer, Token)], + postBody: PostViewJsonV400 + ): ViewJsonV300 = { + val postJson = PostAccountAccessJsonV400(userId, postBody) + val request = (v4_0_0_Request / "banks" / bankId / "accounts" / accountId / "account-access" / "grant").POST <@ (consumerAndToken) + val response = makePostRequest(request, write(postJson)) + Then("We should get a 201 and check the response body") + response.code should equal(201) + response.body.extract[ViewJsonV300] + } + def createTransactionRequest(bankId: String) = { + // Create a Bank + val bank = createBank(bankId) + val addAccountJson1 = SwaggerDefinitionsJSON.createAccountRequestJsonV310 + .copy(user_id = resourceUser1.userId, balance = AmountOfMoneyJsonV121("EUR","0"), + account_routings = List(AccountRoutingJsonV121(Random.nextString(10), Random.nextString(10)))) + val addAccountJson2 = SwaggerDefinitionsJSON.createAccountRequestJsonV310 + .copy(user_id = resourceUser1.userId, balance = AmountOfMoneyJsonV121("EUR","0"), + account_routings = List(AccountRoutingJsonV121(Random.nextString(10), Random.nextString(10)))) + // Create from account + val fromAccount = createAccountViaEndpoint(bank.bankId.value, addAccountJson1, user1) + // Create to account + val toAccount = createAccountViaEndpoint(bank.bankId.value, addAccountJson2, user1) + // Create a custom view + val customViewJson = createViewJsonV300.copy(name = "_cascade_delete", metadata_view = "_cascade_delete", is_public = false).toCreateViewJson + val customView = createViewViaEndpoint(bank.bankId.value, fromAccount.account_id, customViewJson, user1) + // Grant access to the view + grantUserAccessToViewViaEndpoint( + bank.bankId.value, + fromAccount.account_id, + resourceUser1.userId, + user1, + PostViewJsonV400(view_id = customView.id, is_system = false) + ) + // Create a Transaction Request + val transactionRequest = createTransactionRequestViaEndpoint( + fromBankId = bank.bankId.value, + fromAccountId = fromAccount.account_id, + fromCurrency = fromAccount.balance.currency, + fromViewId = customView.id, + amount = "10", + toBankId = bank.bankId.value, + toAccountId = toAccount.account_id, + user1 + ) + val transactionId = transactionRequest.transaction_ids.headOption.getOrElse("") + (bank.bankId.value, fromAccount.account_id, transactionId) + } } \ No newline at end of file From 5c3c6a4f2a8fd53f0f2e12d660dd616af824c712 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Tue, 21 May 2024 13:49:55 +0200 Subject: [PATCH 15/75] feature/Tweak account balances tests v5.1.0 --- .../code/api/v5_1_0/AccountBalanceTest.scala | 26 +++++++++++++++++++ .../code/api/v5_1_0/V510ServerSetup.scala | 6 ++--- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/obp-api/src/test/scala/code/api/v5_1_0/AccountBalanceTest.scala b/obp-api/src/test/scala/code/api/v5_1_0/AccountBalanceTest.scala index 9e99cc527..bcb99ca06 100644 --- a/obp-api/src/test/scala/code/api/v5_1_0/AccountBalanceTest.scala +++ b/obp-api/src/test/scala/code/api/v5_1_0/AccountBalanceTest.scala @@ -2,10 +2,12 @@ package code.api.v5_1_0 import code.api.util.APIUtil.OAuth._ import code.api.util.ErrorMessages.UserNotLoggedIn +import code.api.v4_0_0.{AccountsBalancesJsonV400, BalanceJsonV400} import code.api.v5_1_0.OBPAPI5_1_0.Implementations5_1_0 import com.github.dwickern.macros.NameOf.nameOf import com.openbankproject.commons.model.ErrorMessage import com.openbankproject.commons.util.ApiVersion +import net.liftweb.json import org.scalatest.Tag class AccountBalanceTest extends V510ServerSetup { @@ -61,9 +63,33 @@ class AccountBalanceTest extends V510ServerSetup { } feature(s"test $ApiEndpoint2 version $VersionOfApi - Authorized access with proper view") { scenario("We will call the endpoint with user credentials", VersionOfApi, ApiEndpoint1) { + val responseGetAccountBalances = makeGetRequest(requestGetAccountсBalances("owner") <@ user1) Then("We should get a 200") responseGetAccountBalances.code should equal(200) + val accountsBefore = responseGetAccountBalances.body.extract[AccountsBalancesJsonV400].accounts.length + + // Make transaction + val amountOfMoney = "10.00" + // Create from and to account with 0 balance and transfer money + val (fromAccountId, toAccountId, _) = createTransactionRequest(bankId, amountOfMoney) + + val responseGetAccountBalances2 = makeGetRequest(requestGetAccountсBalances("owner") <@ user1) + Then("We should get a 200") + responseGetAccountBalances2.code should equal(200) + + val accountsAfter = responseGetAccountBalances2.body.extract[AccountsBalancesJsonV400].accounts.length + accountsAfter should equal(accountsBefore + 2) + + val balances = responseGetAccountBalances2.body.extract[AccountsBalancesJsonV400] + + val toAccountBalance = balances.accounts.filter(_.account_id == toAccountId) + val filteredBalances: List[BalanceJsonV400] = toAccountBalance.flatMap(_.balances.filter(i => i.amount == amountOfMoney && i.currency == "EUR")) + filteredBalances.length should equal(1) + + val fromAccountBalance = balances.accounts.filter(_.account_id == fromAccountId) + val filteredFromAccountBalance: List[BalanceJsonV400] = fromAccountBalance.flatMap(_.balances.filter(i => i.amount == s"-${amountOfMoney}" && i.currency == "EUR")) + filteredFromAccountBalance.length should equal(1) } } diff --git a/obp-api/src/test/scala/code/api/v5_1_0/V510ServerSetup.scala b/obp-api/src/test/scala/code/api/v5_1_0/V510ServerSetup.scala index ec214b137..fb612b1a5 100644 --- a/obp-api/src/test/scala/code/api/v5_1_0/V510ServerSetup.scala +++ b/obp-api/src/test/scala/code/api/v5_1_0/V510ServerSetup.scala @@ -138,7 +138,7 @@ trait V510ServerSetup extends ServerSetupWithTestData with DefaultUsers { response.code should equal(201) response.body.extract[ViewJsonV300] } - def createTransactionRequest(bankId: String) = { + def createTransactionRequest(bankId: String, amountOnMoney: String): (String, String, String) = { // Create a Bank val bank = createBank(bankId) val addAccountJson1 = SwaggerDefinitionsJSON.createAccountRequestJsonV310 @@ -168,13 +168,13 @@ trait V510ServerSetup extends ServerSetupWithTestData with DefaultUsers { fromAccountId = fromAccount.account_id, fromCurrency = fromAccount.balance.currency, fromViewId = customView.id, - amount = "10", + amount = amountOnMoney, toBankId = bank.bankId.value, toAccountId = toAccount.account_id, user1 ) val transactionId = transactionRequest.transaction_ids.headOption.getOrElse("") - (bank.bankId.value, fromAccount.account_id, transactionId) + (fromAccount.account_id, toAccount.account_id, transactionId) } } \ No newline at end of file From 45c8e86f085ca6650c16e116807f519cce67b20c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Wed, 22 May 2024 13:11:54 +0200 Subject: [PATCH 16/75] feature/Add endpoint getBankAccountsBalancesThroughView v5.1.0 --- .../scala/code/api/v5_1_0/APIMethods510.scala | 61 ++++++++++++++++--- .../code/remotedata/RemotedataViews.scala | 3 + .../main/scala/code/views/MapperViews.scala | 11 ++++ obp-api/src/main/scala/code/views/Views.scala | 2 + .../code/api/v5_1_0/AccountBalanceTest.scala | 24 ++++++-- 5 files changed, 89 insertions(+), 12 deletions(-) diff --git a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala index 5ff01f51c..ed6d6247b 100644 --- a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala +++ b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala @@ -2181,12 +2181,13 @@ trait APIMethods510 { (Full(u), callContext) <- SS.user bankIdAccountId = BankIdAccountId(bankId, accountId) view <- NewStyle.function.checkViewAccessAndReturnView(viewId, bankIdAccountId, Full(u), callContext) + // Note we do one explicit check here rather than use moderated account because this provide an explicit message failMsg = ViewDoesNotPermitAccess + " You need the permission canSeeBankAccountBalance." _ <- Helper.booleanToFuture(failMsg, 403, cc = callContext) { view.canSeeBankAccountBalance } (accountBalances, callContext) <- NewStyle.function.getBankAccountBalances(bankIdAccountId, callContext) - } yield{ + } yield { (createAccountBalancesJson(accountBalances), HttpCode.`200`(callContext)) } } @@ -2215,12 +2216,50 @@ trait APIMethods510 { cc => implicit val ec = EndpointContext(Some(cc)) for { (Full(u), callContext) <- SS.user - availablePrivateAccounts <- Views.views.vend.getPrivateBankAccountsFuture(u, bankId) // Get all accounts at the bank the user can see - (views, _) = Views.views.vend.privateViewsUserCanAccessAtBank(u, bankId) // Get all views at the bank the user can access - canSeeBankAccountBalanceViews = views.filter(_.canSeeBankAccountBalance) // Filter views which can read the balance - allowedAccounts = availablePrivateAccounts.intersect(canSeeBankAccountBalanceViews.map( i => - BankIdAccountId(i.bankId, i.accountId)) - ) // Filter out accounts the user has not permission to see balances + // Get all views at the bank the user can access + (views, availablePrivateAccounts) = Views.views.vend.privateViewsUserCanAccessAtBank(u, bankId) + // Filter views which can read the balance + canSeeBankAccountBalanceViews = views.filter(_.canSeeBankAccountBalance) + // Filter out accounts the user has not permission to see balances and remove duplicates + allowedAccounts = intersectAccountAccessAndView(availablePrivateAccounts, canSeeBankAccountBalanceViews) + (accountsBalances, callContext) <- NewStyle.function.getBankAccountsBalances(allowedAccounts, callContext) + } yield { + (createBalancesJson(accountsBalances), HttpCode.`200`(callContext)) + } + } + } + + staticResourceDocs += ResourceDoc( + getBankAccountsBalancesThroughView, + implementedInApiVersion, + nameOf(getBankAccountsBalancesThroughView), + "GET", + "/banks/BANK_ID/views/VIEW_ID/balances", + "Get Account Balances by BANK_ID", + """Get the Balances for the Account specified by BANK_ID.""", + EmptyBody, + accountBalancesV400Json, + List( + $UserNotLoggedIn, + $BankNotFound, + UnknownError + ), + apiTagAccount :: apiTagPSD2AIS :: apiTagPsd2 :: Nil + ) + + lazy val getBankAccountsBalancesThroughView : OBPEndpoint = { + case "banks" :: BankId(bankId) :: "views" :: ViewId(viewId) :: "balances" :: Nil JsonGet _ => { + cc => implicit val ec = EndpointContext(Some(cc)) + for { + (Full(u), callContext) <- SS.user + (views, availablePrivateAccounts) = Views.views.vend.privateViewsUserCanAccessAtBankThroughViews(u, bankId, List(viewId)) // Get all views at the bank the user can access + _ <- Helper.booleanToFuture(CannotFindAccountAccess, 403, cc = callContext) { + views.nonEmpty + } + // Filter views which can read the balance + canSeeBankAccountBalanceViews = views.filter(_.canSeeBankAccountBalance) + // Filter out accounts the user has not permission to see balances and remove duplicates + allowedAccounts = intersectAccountAccessAndView(availablePrivateAccounts, canSeeBankAccountBalanceViews) (accountsBalances, callContext) <- NewStyle.function.getBankAccountsBalances(allowedAccounts, callContext) } yield { (createBalancesJson(accountsBalances), HttpCode.`200`(callContext)) @@ -2231,6 +2270,14 @@ trait APIMethods510 { } + + private def intersectAccountAccessAndView(accountAccesses: List[AccountAccess], views: List[View]) = { + accountAccesses.map(item => + BankIdAccountId(BankId(item.bank_id.get), AccountId(item.account_id.get)) + ).intersect(views.map(item => + BankIdAccountId(item.bankId, item.accountId)) + ).distinct + } } object APIMethods510 extends RestHelper with APIMethods510 { diff --git a/obp-api/src/main/scala/code/remotedata/RemotedataViews.scala b/obp-api/src/main/scala/code/remotedata/RemotedataViews.scala index cef608ac0..fa18d9ce1 100644 --- a/obp-api/src/main/scala/code/remotedata/RemotedataViews.scala +++ b/obp-api/src/main/scala/code/remotedata/RemotedataViews.scala @@ -119,6 +119,9 @@ object RemotedataViews extends ObpActorInit with Views { def privateViewsUserCanAccessAtBank(user: User, bankId: BankId): (List[View], List[AccountAccess]) = getValueFromFuture( (actor ? cc.privateViewsUserCanAccessAtBank(user, bankId)).mapTo[(List[View], List[AccountAccess])] ) + def privateViewsUserCanAccessAtBankThroughViews(user: User, bankId: BankId, viewIds: List[ViewId]): ((List[View], List[AccountAccess])) = getValueFromFuture( + (actor ? cc.privateViewsUserCanAccessAtBankThroughViews(user, bankId, viewIds)).mapTo[(List[View], List[AccountAccess])] + ) def privateViewsUserCanAccessForAccount(user: User, bankIdAccountId : BankIdAccountId): List[View] = getValueFromFuture( (actor ? cc.privateViewsUserCanAccessForAccount(user: User, bankIdAccountId : BankIdAccountId)).mapTo[List[View]] diff --git a/obp-api/src/main/scala/code/views/MapperViews.scala b/obp-api/src/main/scala/code/views/MapperViews.scala index 783a124d8..648121152 100644 --- a/obp-api/src/main/scala/code/views/MapperViews.scala +++ b/obp-api/src/main/scala/code/views/MapperViews.scala @@ -584,6 +584,17 @@ object MapperViews extends Views with MdcLoggable { }) PrivateViewsUserCanAccessCommon(accountAccess) } + def privateViewsUserCanAccessAtBankThroughViews(user: User, bankId: BankId, viewIds: List[ViewId]): (List[View], List[AccountAccess]) ={ + val accountAccess = AccountAccess.findAll( + By(AccountAccess.user_fk, user.userPrimaryKey.value), + By(AccountAccess.bank_id, bankId.value), + ByList(AccountAccess.view_id, viewIds.map(_.value)) + ).filter(accountAccess => { + val view = getViewFromAccountAccess(accountAccess) + view.isDefined && view.map(_.isPrivate) == Full(true) + }) + PrivateViewsUserCanAccessCommon(accountAccess) + } private def PrivateViewsUserCanAccessCommon(accountAccess: List[AccountAccess]): (List[ViewDefinition], List[AccountAccess]) = { val listOfTuples: List[(AccountAccess, Box[ViewDefinition])] = accountAccess.map( diff --git a/obp-api/src/main/scala/code/views/Views.scala b/obp-api/src/main/scala/code/views/Views.scala index 49360e057..a3c1570b3 100644 --- a/obp-api/src/main/scala/code/views/Views.scala +++ b/obp-api/src/main/scala/code/views/Views.scala @@ -84,6 +84,7 @@ trait Views { def privateViewsUserCanAccess(user: User): (List[View], List[AccountAccess]) def privateViewsUserCanAccess(user: User, viewIds: List[ViewId]): (List[View], List[AccountAccess]) def privateViewsUserCanAccessAtBank(user: User, bankId: BankId): (List[View], List[AccountAccess]) + def privateViewsUserCanAccessAtBankThroughViews(user: User, bankId: BankId, viewIds: List[ViewId]): (List[View], List[AccountAccess]) def privateViewsUserCanAccessForAccount(user: User, bankIdAccountId : BankIdAccountId) : List[View] //the following return list[BankIdAccountId], just use the list[View] method, the View object contains enough data for it. @@ -150,6 +151,7 @@ class RemotedataViewsCaseClasses { case class privateViewsUserCanAccess(user: User) case class privateViewsUserCanAccessViaViewId(user: User, viewIds: List[ViewId]) case class privateViewsUserCanAccessAtBank(user: User, bankId: BankId) + case class privateViewsUserCanAccessAtBankThroughViews(user: User, bankId: BankId, viewIds: List[ViewId]) case class privateViewsUserCanAccessForAccount(user: User, bankIdAccountId : BankIdAccountId) case class getAllFirehoseAccounts(bank: Bank, user : User) case class publicViews() diff --git a/obp-api/src/test/scala/code/api/v5_1_0/AccountBalanceTest.scala b/obp-api/src/test/scala/code/api/v5_1_0/AccountBalanceTest.scala index bcb99ca06..5c102103f 100644 --- a/obp-api/src/test/scala/code/api/v5_1_0/AccountBalanceTest.scala +++ b/obp-api/src/test/scala/code/api/v5_1_0/AccountBalanceTest.scala @@ -7,6 +7,7 @@ import code.api.v5_1_0.OBPAPI5_1_0.Implementations5_1_0 import com.github.dwickern.macros.NameOf.nameOf import com.openbankproject.commons.model.ErrorMessage import com.openbankproject.commons.util.ApiVersion +import dispatch.Req import net.liftweb.json import org.scalatest.Tag @@ -21,11 +22,13 @@ class AccountBalanceTest extends V510ServerSetup { object VersionOfApi extends Tag(ApiVersion.v5_1_0.toString) object ApiEndpoint1 extends Tag(nameOf(Implementations5_1_0.getBankAccountBalances)) object ApiEndpoint2 extends Tag(nameOf(Implementations5_1_0.getBankAccountsBalances)) + object ApiEndpoint3 extends Tag(nameOf(Implementations5_1_0.getBankAccountsBalancesThroughView)) lazy val bankId = randomBankId lazy val bankAccount = randomPrivateAccountViaEndpoint(bankId) - def requestGetAccountBalances(viewId: String = "None") = (v5_1_0_Request / "banks" / bankAccount.bank_id / "accounts" / bankAccount.id / "views" / viewId / "balances").GET - def requestGetAccountсBalances(viewId: String = "None") = (v5_1_0_Request / "banks" / bankAccount.bank_id / "balances").GET + def requestGetAccountBalances(viewId: String = "None"): Req = (v5_1_0_Request / "banks" / bankAccount.bank_id / "accounts" / bankAccount.id / "views" / viewId / "balances").GET + def requestGetAccountsBalances(): Req = (v5_1_0_Request / "banks" / bankAccount.bank_id / "balances").GET + def requestGetAccountsBalancesThroughView(viewId: String = "None"): Req = (v5_1_0_Request / "banks" / bankAccount.bank_id / "views" / viewId / "balances").GET feature(s"test $ApiEndpoint1 version $VersionOfApi - Unauthorized access") { scenario("We will call the endpoint without user credentials", ApiEndpoint1, VersionOfApi) { @@ -55,7 +58,7 @@ class AccountBalanceTest extends V510ServerSetup { feature(s"test $ApiEndpoint2 version $VersionOfApi - Unauthorized access") { scenario("We will call the endpoint without user credentials", ApiEndpoint1, VersionOfApi) { When(s"We make a request $ApiEndpoint1") - val responseGetAccountBalances = makeGetRequest(requestGetAccountсBalances()) + val responseGetAccountBalances = makeGetRequest(requestGetAccountsBalances()) Then("We should get a 401") responseGetAccountBalances.code should equal(401) responseGetAccountBalances.body.extract[ErrorMessage].message should equal(UserNotLoggedIn) @@ -64,7 +67,7 @@ class AccountBalanceTest extends V510ServerSetup { feature(s"test $ApiEndpoint2 version $VersionOfApi - Authorized access with proper view") { scenario("We will call the endpoint with user credentials", VersionOfApi, ApiEndpoint1) { - val responseGetAccountBalances = makeGetRequest(requestGetAccountсBalances("owner") <@ user1) + val responseGetAccountBalances = makeGetRequest(requestGetAccountsBalances() <@ user1) Then("We should get a 200") responseGetAccountBalances.code should equal(200) val accountsBefore = responseGetAccountBalances.body.extract[AccountsBalancesJsonV400].accounts.length @@ -74,7 +77,7 @@ class AccountBalanceTest extends V510ServerSetup { // Create from and to account with 0 balance and transfer money val (fromAccountId, toAccountId, _) = createTransactionRequest(bankId, amountOfMoney) - val responseGetAccountBalances2 = makeGetRequest(requestGetAccountсBalances("owner") <@ user1) + val responseGetAccountBalances2 = makeGetRequest(requestGetAccountsBalances() <@ user1) Then("We should get a 200") responseGetAccountBalances2.code should equal(200) @@ -94,4 +97,15 @@ class AccountBalanceTest extends V510ServerSetup { } + feature(s"test $ApiEndpoint3 version $VersionOfApi - Unauthorized access") { + scenario("We will call the endpoint without user credentials", ApiEndpoint1, VersionOfApi) { + When(s"We make a request $ApiEndpoint1") + val responseGetAccountBalances = makeGetRequest(requestGetAccountsBalancesThroughView("owner")) + Then("We should get a 401") + responseGetAccountBalances.code should equal(401) + responseGetAccountBalances.body.extract[ErrorMessage].message should equal(UserNotLoggedIn) + } + } + + } From da08ded75b7172e8a0d912e7a5b587045f29ae79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Wed, 22 May 2024 18:45:24 +0200 Subject: [PATCH 17/75] feature/Tweak endpoint getBankAccountsBalancesThroughView v5.1.0 --- .../main/scala/code/api/v5_1_0/APIMethods510.scala | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala index ed6d6247b..f6f981212 100644 --- a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala +++ b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala @@ -2271,12 +2271,13 @@ trait APIMethods510 { } - private def intersectAccountAccessAndView(accountAccesses: List[AccountAccess], views: List[View]) = { - accountAccesses.map(item => - BankIdAccountId(BankId(item.bank_id.get), AccountId(item.account_id.get)) - ).intersect(views.map(item => - BankIdAccountId(item.bankId, item.accountId)) - ).distinct + private def intersectAccountAccessAndView(accountAccesses: List[AccountAccess], views: List[View]): List[BankIdAccountId] = { + val intersectedViewIds = accountAccesses.map(item => item.view_id.get) + .intersect(views.map(item => item.viewId.value)).distinct + accountAccesses + .filter(i => intersectedViewIds.contains(i.view_id.get)) + .map(item => BankIdAccountId(BankId(item.bank_id.get), AccountId(item.account_id.get))) + .distinct } } From 821e96f72f01f94b55e5d6fadf0a9722e0e1bf1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Thu, 23 May 2024 09:22:39 +0200 Subject: [PATCH 18/75] feature/Add function getAccountAccessAtBankThroughView --- .../scala/code/api/v5_1_0/APIMethods510.scala | 19 ++++++++++--------- .../code/remotedata/RemotedataViews.scala | 2 +- .../main/scala/code/views/MapperViews.scala | 2 +- obp-api/src/main/scala/code/views/Views.scala | 4 +++- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala index f6f981212..d9d6d9da2 100644 --- a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala +++ b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala @@ -2216,12 +2216,12 @@ trait APIMethods510 { cc => implicit val ec = EndpointContext(Some(cc)) for { (Full(u), callContext) <- SS.user - // Get all views at the bank the user can access - (views, availablePrivateAccounts) = Views.views.vend.privateViewsUserCanAccessAtBank(u, bankId) + // Get all account accesses at the bank for the user + (views, accountAccesses) = Views.views.vend.privateViewsUserCanAccessAtBank(u, bankId) // Filter views which can read the balance canSeeBankAccountBalanceViews = views.filter(_.canSeeBankAccountBalance) - // Filter out accounts the user has not permission to see balances and remove duplicates - allowedAccounts = intersectAccountAccessAndView(availablePrivateAccounts, canSeeBankAccountBalanceViews) + // Filter accounts the user has permission to see balances and remove duplicates + allowedAccounts = intersectAccountAccessAndView(accountAccesses, canSeeBankAccountBalanceViews) (accountsBalances, callContext) <- NewStyle.function.getBankAccountsBalances(allowedAccounts, callContext) } yield { (createBalancesJson(accountsBalances), HttpCode.`200`(callContext)) @@ -2252,14 +2252,15 @@ trait APIMethods510 { cc => implicit val ec = EndpointContext(Some(cc)) for { (Full(u), callContext) <- SS.user - (views, availablePrivateAccounts) = Views.views.vend.privateViewsUserCanAccessAtBankThroughViews(u, bankId, List(viewId)) // Get all views at the bank the user can access + // Get all account accesses at the bank for the user + (views, accountAccesses) = Views.views.vend.getAccountAccessAtBankThroughView(u, bankId, viewId) _ <- Helper.booleanToFuture(CannotFindAccountAccess, 403, cc = callContext) { views.nonEmpty } // Filter views which can read the balance canSeeBankAccountBalanceViews = views.filter(_.canSeeBankAccountBalance) - // Filter out accounts the user has not permission to see balances and remove duplicates - allowedAccounts = intersectAccountAccessAndView(availablePrivateAccounts, canSeeBankAccountBalanceViews) + // Filter accounts the user has permission to see balances and remove duplicates + allowedAccounts = intersectAccountAccessAndView(accountAccesses, canSeeBankAccountBalanceViews) (accountsBalances, callContext) <- NewStyle.function.getBankAccountsBalances(allowedAccounts, callContext) } yield { (createBalancesJson(accountsBalances), HttpCode.`200`(callContext)) @@ -2273,11 +2274,11 @@ trait APIMethods510 { private def intersectAccountAccessAndView(accountAccesses: List[AccountAccess], views: List[View]): List[BankIdAccountId] = { val intersectedViewIds = accountAccesses.map(item => item.view_id.get) - .intersect(views.map(item => item.viewId.value)).distinct + .intersect(views.map(item => item.viewId.value)).distinct // Join view definition and account access via view_id accountAccesses .filter(i => intersectedViewIds.contains(i.view_id.get)) .map(item => BankIdAccountId(BankId(item.bank_id.get), AccountId(item.account_id.get))) - .distinct + .distinct // List pairs (bank_id, account_id) } } diff --git a/obp-api/src/main/scala/code/remotedata/RemotedataViews.scala b/obp-api/src/main/scala/code/remotedata/RemotedataViews.scala index fa18d9ce1..9400bf194 100644 --- a/obp-api/src/main/scala/code/remotedata/RemotedataViews.scala +++ b/obp-api/src/main/scala/code/remotedata/RemotedataViews.scala @@ -119,7 +119,7 @@ object RemotedataViews extends ObpActorInit with Views { def privateViewsUserCanAccessAtBank(user: User, bankId: BankId): (List[View], List[AccountAccess]) = getValueFromFuture( (actor ? cc.privateViewsUserCanAccessAtBank(user, bankId)).mapTo[(List[View], List[AccountAccess])] ) - def privateViewsUserCanAccessAtBankThroughViews(user: User, bankId: BankId, viewIds: List[ViewId]): ((List[View], List[AccountAccess])) = getValueFromFuture( + def getAccountAccessAtBankThroughViews(user: User, bankId: BankId, viewIds: List[ViewId]): ((List[View], List[AccountAccess])) = getValueFromFuture( (actor ? cc.privateViewsUserCanAccessAtBankThroughViews(user, bankId, viewIds)).mapTo[(List[View], List[AccountAccess])] ) diff --git a/obp-api/src/main/scala/code/views/MapperViews.scala b/obp-api/src/main/scala/code/views/MapperViews.scala index 648121152..2f03028c2 100644 --- a/obp-api/src/main/scala/code/views/MapperViews.scala +++ b/obp-api/src/main/scala/code/views/MapperViews.scala @@ -584,7 +584,7 @@ object MapperViews extends Views with MdcLoggable { }) PrivateViewsUserCanAccessCommon(accountAccess) } - def privateViewsUserCanAccessAtBankThroughViews(user: User, bankId: BankId, viewIds: List[ViewId]): (List[View], List[AccountAccess]) ={ + def getAccountAccessAtBankThroughViews(user: User, bankId: BankId, viewIds: List[ViewId]): (List[View], List[AccountAccess]) ={ val accountAccess = AccountAccess.findAll( By(AccountAccess.user_fk, user.userPrimaryKey.value), By(AccountAccess.bank_id, bankId.value), diff --git a/obp-api/src/main/scala/code/views/Views.scala b/obp-api/src/main/scala/code/views/Views.scala index a3c1570b3..362c3289d 100644 --- a/obp-api/src/main/scala/code/views/Views.scala +++ b/obp-api/src/main/scala/code/views/Views.scala @@ -84,7 +84,9 @@ trait Views { def privateViewsUserCanAccess(user: User): (List[View], List[AccountAccess]) def privateViewsUserCanAccess(user: User, viewIds: List[ViewId]): (List[View], List[AccountAccess]) def privateViewsUserCanAccessAtBank(user: User, bankId: BankId): (List[View], List[AccountAccess]) - def privateViewsUserCanAccessAtBankThroughViews(user: User, bankId: BankId, viewIds: List[ViewId]): (List[View], List[AccountAccess]) + def getAccountAccessAtBankThroughView(user: User, bankId: BankId, viewId: ViewId): (List[View], List[AccountAccess]) = + getAccountAccessAtBankThroughViews(user, bankId, List(viewId)) + def getAccountAccessAtBankThroughViews(user: User, bankId: BankId, viewIds: List[ViewId]): (List[View], List[AccountAccess]) def privateViewsUserCanAccessForAccount(user: User, bankIdAccountId : BankIdAccountId) : List[View] //the following return list[BankIdAccountId], just use the list[View] method, the View object contains enough data for it. From ad0644a80603ecac7e9e10c760bafefe571a0030 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Thu, 23 May 2024 15:24:17 +0200 Subject: [PATCH 19/75] feature/Secure endpoints v4.0.0 related to balance --- .../main/scala/code/api/util/APIUtil.scala | 10 +++++++ .../scala/code/api/v4_0_0/APIMethods400.scala | 27 ++++++++++++++----- .../scala/code/api/v5_1_0/APIMethods510.scala | 12 +-------- 3 files changed, 31 insertions(+), 18 deletions(-) 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 eb9c71899..73079dc6d 100644 --- a/obp-api/src/main/scala/code/api/util/APIUtil.scala +++ b/obp-api/src/main/scala/code/api/util/APIUtil.scala @@ -122,6 +122,7 @@ import code.api.v2_1_0.OBPAPI2_1_0.Implementations2_1_0 import code.api.v2_2_0.OBPAPI2_2_0.Implementations2_2_0 import code.etag.MappedETag import code.users.Users +import code.views.system.AccountAccess import net.liftweb.mapper.By import scala.collection.mutable @@ -4925,4 +4926,13 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{ else UserLacksPermissionCanGrantAccessToCustomViewForTargetAccount + s"Current source viewId(${sourceViewId.value}) and target viewId (${targetViewId.value})" + + def intersectAccountAccessAndView(accountAccesses: List[AccountAccess], views: List[View]): List[BankIdAccountId] = { + val intersectedViewIds = accountAccesses.map(item => item.view_id.get) + .intersect(views.map(item => item.viewId.value)).distinct // Join view definition and account access via view_id + accountAccesses + .filter(i => intersectedViewIds.contains(i.view_id.get)) + .map(item => BankIdAccountId(BankId(item.bank_id.get), AccountId(item.account_id.get))) + .distinct // List pairs (bank_id, account_id) + } } \ No newline at end of file diff --git a/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala b/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala index 98d10cf31..e2978733f 100644 --- a/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala +++ b/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala @@ -3364,9 +3364,14 @@ trait APIMethods400 extends MdcLoggable { cc => implicit val ec = EndpointContext(Some(cc)) for { (Full(u), callContext) <- SS.user - availablePrivateAccounts <- Views.views.vend.getPrivateBankAccountsFuture(u, bankId) - (accountsBalances, callContext)<- NewStyle.function.getBankAccountsBalances(availablePrivateAccounts, callContext) - } yield{ + // Get all account accesses at the bank for the user + (views, accountAccesses) = Views.views.vend.privateViewsUserCanAccessAtBank(u, bankId) + // Filter views which can read the balance + canSeeBankAccountBalanceViews = views.filter(_.canSeeBankAccountBalance) + // Filter accounts the user has permission to see balances and remove duplicates + allowedAccounts = APIUtil.intersectAccountAccessAndView(accountAccesses, canSeeBankAccountBalanceViews) + (accountsBalances, callContext)<- NewStyle.function.getBankAccountsBalances(allowedAccounts, callContext) + } yield { (createBalancesJson(accountsBalances), HttpCode.`200`(callContext)) } } @@ -3391,10 +3396,18 @@ trait APIMethods400 extends MdcLoggable { cc => implicit val ec = EndpointContext(Some(cc)) for { (Full(u), callContext) <- SS.user - availablePrivateAccounts <- Views.views.vend.getPrivateBankAccountsFuture(u, bankId) - bankIdAcconutId <- NewStyle.function.tryons(s"$CannotFindAccountAccess AccountId(${accountId.value})", 400, cc.callContext) {availablePrivateAccounts.find(_.accountId==accountId).get} - (accountBalances, callContext)<- NewStyle.function.getBankAccountBalances(bankIdAcconutId, callContext) - } yield{ + // Get all account accesses at the bank for the user + (views, accountAccesses) = Views.views.vend.privateViewsUserCanAccessAtBank(u, bankId) + // Filter views which can read the balance + canSeeBankAccountBalanceViews = views.filter(_.canSeeBankAccountBalance) + // Filter accounts the user has permission to see balances and remove duplicates + allowedAccounts = APIUtil.intersectAccountAccessAndView(accountAccesses, canSeeBankAccountBalanceViews) + msg = s"$CannotFindAccountAccess AccountId(${accountId.value})" + bankIdAccountId <- NewStyle.function.tryons(msg, 400, cc.callContext) { + allowedAccounts.find(_.accountId==accountId).get + } + (accountBalances, callContext)<- NewStyle.function.getBankAccountBalances(bankIdAccountId, callContext) + } yield { (createAccountBalancesJson(accountBalances), HttpCode.`200`(callContext)) } } diff --git a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala index d9d6d9da2..c155e64ce 100644 --- a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala +++ b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala @@ -2260,7 +2260,7 @@ trait APIMethods510 { // Filter views which can read the balance canSeeBankAccountBalanceViews = views.filter(_.canSeeBankAccountBalance) // Filter accounts the user has permission to see balances and remove duplicates - allowedAccounts = intersectAccountAccessAndView(accountAccesses, canSeeBankAccountBalanceViews) + allowedAccounts = APIUtil.intersectAccountAccessAndView(accountAccesses, canSeeBankAccountBalanceViews) (accountsBalances, callContext) <- NewStyle.function.getBankAccountsBalances(allowedAccounts, callContext) } yield { (createBalancesJson(accountsBalances), HttpCode.`200`(callContext)) @@ -2269,16 +2269,6 @@ trait APIMethods510 { } - - } - - private def intersectAccountAccessAndView(accountAccesses: List[AccountAccess], views: List[View]): List[BankIdAccountId] = { - val intersectedViewIds = accountAccesses.map(item => item.view_id.get) - .intersect(views.map(item => item.viewId.value)).distinct // Join view definition and account access via view_id - accountAccesses - .filter(i => intersectedViewIds.contains(i.view_id.get)) - .map(item => BankIdAccountId(BankId(item.bank_id.get), AccountId(item.account_id.get))) - .distinct // List pairs (bank_id, account_id) } } From f134b86a868822278b47dbbeae5ca1c89bfcc378 Mon Sep 17 00:00:00 2001 From: Simon Redfern Date: Fri, 24 May 2024 10:29:12 +0200 Subject: [PATCH 20/75] tweaking Account Access glossary item --- obp-api/src/main/scala/code/api/util/Glossary.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/obp-api/src/main/scala/code/api/util/Glossary.scala b/obp-api/src/main/scala/code/api/util/Glossary.scala index be899fe1a..2849e74a5 100644 --- a/obp-api/src/main/scala/code/api/util/Glossary.scala +++ b/obp-api/src/main/scala/code/api/util/Glossary.scala @@ -3009,10 +3009,10 @@ object Glossary extends MdcLoggable { title = "Account Access", description = s""" - |Account Access is OBP View system. The Account owners can create the view themselves. - |And they can grant/revoke the view to other users to use their view. + |Account Access governs access to Bank Accounts by end Users. It is an intersecting entity between the User and the View Definition. + |A User must have at least one Account Access record record in order to interact with a Bank Account over the OBP API. |""".stripMargin) - + // val allTagNames: Set[String] = ApiTag.allDisplayTagNames // val existingItems: Set[String] = glossaryItems.map(_.title).toSet // allTagNames.diff(existingItems).map(title => glossaryItems += GlossaryItem(title, title)) From 2376b1d4533f09849eda3c845ef78548b211e8b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Fri, 24 May 2024 16:25:10 +0200 Subject: [PATCH 21/75] refactor/Simplify get balance endpoints --- .../AccountInformationServiceAISApi.scala | 6 +- .../main/scala/code/api/util/NewStyle.scala | 12 ---- .../api/util/newstyle/BalanceNewStyle.scala | 60 +++++++++++++++++++ .../scala/code/api/v3_1_0/APIMethods310.scala | 3 +- .../scala/code/api/v4_0_0/APIMethods400.scala | 20 ++----- .../scala/code/api/v5_1_0/APIMethods510.scala | 24 ++------ .../code/remotedata/RemotedataViews.scala | 4 +- .../main/scala/code/views/MapperViews.scala | 4 +- obp-api/src/main/scala/code/views/Views.scala | 6 +- 9 files changed, 82 insertions(+), 57 deletions(-) create mode 100644 obp-api/src/main/scala/code/api/util/newstyle/BalanceNewStyle.scala diff --git a/obp-api/src/main/scala/code/api/berlin/group/v1_3/AccountInformationServiceAISApi.scala b/obp-api/src/main/scala/code/api/berlin/group/v1_3/AccountInformationServiceAISApi.scala index 0c7d68ea6..b2fe2b1f2 100644 --- a/obp-api/src/main/scala/code/api/berlin/group/v1_3/AccountInformationServiceAISApi.scala +++ b/obp-api/src/main/scala/code/api/berlin/group/v1_3/AccountInformationServiceAISApi.scala @@ -1,7 +1,6 @@ package code.api.builder.AccountInformationServiceAISApi import java.text.SimpleDateFormat - import code.api.APIFailureNewStyle import code.api.Constant.{SYSTEM_READ_ACCOUNTS_BERLIN_GROUP_VIEW_ID, SYSTEM_READ_BALANCES_BERLIN_GROUP_VIEW_ID, SYSTEM_READ_TRANSACTIONS_BERLIN_GROUP_VIEW_ID} import code.api.berlin.group.v1_3.JSONFactory_BERLIN_GROUP_1_3.{PostConsentResponseJson, _} @@ -12,6 +11,7 @@ import code.api.util.APIUtil.{passesPsd2Aisp, _} import code.api.util.ApiTag._ import code.api.util.ErrorMessages._ import code.api.util.NewStyle.HttpCode +import code.api.util.newstyle.BalanceNewStyle import code.api.util.{APIUtil, ApiTag, CallContext, Consent, ExampleValue, NewStyle} import code.bankconnectors.Connector import code.consent.{ConsentStatus, Consents} @@ -367,7 +367,7 @@ The account-id is constant at least throughout the lifecycle of a given consent. _ <- passesPsd2Aisp(callContext) (account: BankAccount, callContext) <- NewStyle.function.getBankAccountByAccountId(accountId, callContext) _ <- checkAccountAccess(ViewId(SYSTEM_READ_BALANCES_BERLIN_GROUP_VIEW_ID), u, account, callContext) - (accountBalances, callContext)<- NewStyle.function.getBankAccountBalances(BankIdAccountId(account.bankId,account.accountId), callContext) + (accountBalances, callContext)<- BalanceNewStyle.getBankAccountBalances(BankIdAccountId(account.bankId,account.accountId), callContext) } yield { (JSONFactory_BERLIN_GROUP_1_3.createAccountBalanceJSON(account, accountBalances), HttpCode.`200`(callContext)) } @@ -482,7 +482,7 @@ This account-id then can be retrieved by the _ <- passesPsd2Aisp(callContext) (account: BankAccount, callContext) <- NewStyle.function.getBankAccountByAccountId(AccountId(accountId), callContext) _ <- checkAccountAccess(ViewId(SYSTEM_READ_BALANCES_BERLIN_GROUP_VIEW_ID), u, account, callContext) - (accountBalances, callContext)<- NewStyle.function.getBankAccountBalances(BankIdAccountId(account.bankId,account.accountId), callContext) + (accountBalances, callContext)<- BalanceNewStyle.getBankAccountBalances(BankIdAccountId(account.bankId,account.accountId), callContext) } yield { (JSONFactory_BERLIN_GROUP_1_3.createCardAccountBalanceJSON(account, accountBalances), HttpCode.`200`(callContext)) } diff --git a/obp-api/src/main/scala/code/api/util/NewStyle.scala b/obp-api/src/main/scala/code/api/util/NewStyle.scala index bbd0ae290..f149bea63 100644 --- a/obp-api/src/main/scala/code/api/util/NewStyle.scala +++ b/obp-api/src/main/scala/code/api/util/NewStyle.scala @@ -383,24 +383,12 @@ object NewStyle extends MdcLoggable{ } } - def getBankAccountsBalances(bankIdAccountIds: List[BankIdAccountId], callContext: Option[CallContext]): OBPReturnType[AccountsBalances] = { - Connector.connector.vend.getBankAccountsBalances(bankIdAccountIds: List[BankIdAccountId], callContext: Option[CallContext]) map { i => - (unboxFullOrFail(i._1, callContext,s"$InvalidConnectorResponseForGetBankAccounts", 400 ), i._2) - } - } - def getBankAccountsWithAttributes(bankId: BankId, queryParams: List[OBPQueryParam], callContext: Option[CallContext]): OBPReturnType[List[FastFirehoseAccount]] = { Connector.connector.vend.getBankAccountsWithAttributes(bankId, queryParams, callContext) map { i => (unboxFullOrFail(i._1, callContext,s"$InvalidConnectorResponseForGetBankAccountsWithAttributes", 400 ), i._2) } } - def getBankAccountBalances(bankIdAccountId: BankIdAccountId, callContext: Option[CallContext]): OBPReturnType[AccountBalances] = { - Connector.connector.vend.getBankAccountBalances(bankIdAccountId: BankIdAccountId, callContext: Option[CallContext]) map { i => - (unboxFullOrFail(i._1, callContext,s"$InvalidConnectorResponseForGetBankAccounts", 400 ), i._2) - } - } - def getAccountRouting(bankId: Option[BankId], scheme: String, address: String, callContext: Option[CallContext]) : OBPReturnType[BankAccountRouting] = { Future(Connector.connector.vend.getAccountRouting(bankId: Option[BankId], scheme: String, address : String, callContext: Option[CallContext])) map { i => unboxFullOrFail(i, callContext,s"$AccountRoutingNotFound Current scheme is $scheme, current address is $address, current bankId is $bankId", 404 ) diff --git a/obp-api/src/main/scala/code/api/util/newstyle/BalanceNewStyle.scala b/obp-api/src/main/scala/code/api/util/newstyle/BalanceNewStyle.scala new file mode 100644 index 000000000..ac7028151 --- /dev/null +++ b/obp-api/src/main/scala/code/api/util/newstyle/BalanceNewStyle.scala @@ -0,0 +1,60 @@ +package code.api.util.newstyle + +import code.api.util.APIUtil.{OBPReturnType, unboxFullOrFail} +import code.api.util.ErrorMessages.InvalidConnectorResponseForGetBankAccounts +import code.api.util.{APIUtil, CallContext} +import code.bankconnectors.Connector +import code.views.Views +import com.openbankproject.commons.model.{AccountBalances, AccountsBalances, BankId, BankIdAccountId, User, ViewId} + +import scala.concurrent.Future + +object BalanceNewStyle { + + import com.openbankproject.commons.ExecutionContext.Implicits.global + + def getAccountAccessAtBankThroughView(user: User, + bankId: BankId, + viewId: ViewId, + callContext: Option[CallContext]): OBPReturnType[List[BankIdAccountId]] = { + Future { + val (views, accountAccesses) = Views.views.vend.getAccountAccessAtBankThroughView(user, bankId, viewId) + // Filter views which can read the balance + val canSeeBankAccountBalanceViews = views.filter(_.canSeeBankAccountBalance) + // Filter accounts the user has permission to see balances and remove duplicates + val allowedAccounts = APIUtil.intersectAccountAccessAndView(accountAccesses, canSeeBankAccountBalanceViews) + allowedAccounts + } map { + (_, callContext) + } + } + + def getAccountAccessAtBank(user: User, + bankId: BankId, + callContext: Option[CallContext]): OBPReturnType[List[BankIdAccountId]] = { + Future { + val (views, accountAccesses) = Views.views.vend.privateViewsUserCanAccessAtBank(user, bankId) + // Filter views which can read the balance + val canSeeBankAccountBalanceViews = views.filter(_.canSeeBankAccountBalance) + // Filter accounts the user has permission to see balances and remove duplicates + val allowedAccounts = APIUtil.intersectAccountAccessAndView(accountAccesses, canSeeBankAccountBalanceViews) + allowedAccounts + } map { + (_, callContext) + } + } + + def getBankAccountBalances(bankIdAccountId: BankIdAccountId, callContext: Option[CallContext]): OBPReturnType[AccountBalances] = { + Connector.connector.vend.getBankAccountBalances(bankIdAccountId: BankIdAccountId, callContext: Option[CallContext]) map { i => + (unboxFullOrFail(i._1, callContext,s"$InvalidConnectorResponseForGetBankAccounts", 400 ), i._2) + } + } + + def getBankAccountsBalances(bankIdAccountIds: List[BankIdAccountId], callContext: Option[CallContext]): OBPReturnType[AccountsBalances] = { + Connector.connector.vend.getBankAccountsBalances(bankIdAccountIds: List[BankIdAccountId], callContext: Option[CallContext]) map { i => + (unboxFullOrFail(i._1, callContext,s"$InvalidConnectorResponseForGetBankAccounts", 400 ), i._2) + } + } + + +} diff --git a/obp-api/src/main/scala/code/api/v3_1_0/APIMethods310.scala b/obp-api/src/main/scala/code/api/v3_1_0/APIMethods310.scala index 77f64364b..74035de76 100644 --- a/obp-api/src/main/scala/code/api/v3_1_0/APIMethods310.scala +++ b/obp-api/src/main/scala/code/api/v3_1_0/APIMethods310.scala @@ -17,6 +17,7 @@ import code.api.util.ExampleValue._ import code.api.util.FutureUtil.EndpointContext import code.api.util.NewStyle.HttpCode import code.api.util._ +import code.api.util.newstyle.BalanceNewStyle import code.api.v1_2_1.{JSONFactory, RateLimiting} import code.api.v2_0_0.CreateMeetingJson import code.api.v2_1_0._ @@ -5935,7 +5936,7 @@ trait APIMethods310 { (Full(u), callContext) <- authenticatedAccess(cc) (_, callContext) <- NewStyle.function.getBank(bankId, callContext) availablePrivateAccounts <- Views.views.vend.getPrivateBankAccountsFuture(u, bankId) - (accountsBalances, callContext)<- NewStyle.function.getBankAccountsBalances(availablePrivateAccounts, callContext) + (accountsBalances, callContext)<- BalanceNewStyle.getBankAccountsBalances(availablePrivateAccounts, callContext) } yield{ (createBalancesJson(accountsBalances), HttpCode.`200`(callContext)) } diff --git a/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala b/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala index e2978733f..0dafbb217 100644 --- a/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala +++ b/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala @@ -29,7 +29,7 @@ import code.api.util._ import code.api.util.migration.Migration import code.api.util.newstyle.AttributeDefinition._ import code.api.util.newstyle.Consumer._ -import code.api.util.newstyle.UserCustomerLinkNewStyle +import code.api.util.newstyle.{BalanceNewStyle, UserCustomerLinkNewStyle} import code.api.util.newstyle.UserCustomerLinkNewStyle.getUserCustomerLinks import code.api.v1_2_1.{JSONFactory, PostTransactionTagJSON} import code.api.v1_4_0.JSONFactory1_4_0 @@ -3364,13 +3364,8 @@ trait APIMethods400 extends MdcLoggable { cc => implicit val ec = EndpointContext(Some(cc)) for { (Full(u), callContext) <- SS.user - // Get all account accesses at the bank for the user - (views, accountAccesses) = Views.views.vend.privateViewsUserCanAccessAtBank(u, bankId) - // Filter views which can read the balance - canSeeBankAccountBalanceViews = views.filter(_.canSeeBankAccountBalance) - // Filter accounts the user has permission to see balances and remove duplicates - allowedAccounts = APIUtil.intersectAccountAccessAndView(accountAccesses, canSeeBankAccountBalanceViews) - (accountsBalances, callContext)<- NewStyle.function.getBankAccountsBalances(allowedAccounts, callContext) + (allowedAccounts, callContext) <- BalanceNewStyle.getAccountAccessAtBank(u, bankId, callContext) + (accountsBalances, callContext)<- BalanceNewStyle.getBankAccountsBalances(allowedAccounts, callContext) } yield { (createBalancesJson(accountsBalances), HttpCode.`200`(callContext)) } @@ -3396,17 +3391,12 @@ trait APIMethods400 extends MdcLoggable { cc => implicit val ec = EndpointContext(Some(cc)) for { (Full(u), callContext) <- SS.user - // Get all account accesses at the bank for the user - (views, accountAccesses) = Views.views.vend.privateViewsUserCanAccessAtBank(u, bankId) - // Filter views which can read the balance - canSeeBankAccountBalanceViews = views.filter(_.canSeeBankAccountBalance) - // Filter accounts the user has permission to see balances and remove duplicates - allowedAccounts = APIUtil.intersectAccountAccessAndView(accountAccesses, canSeeBankAccountBalanceViews) + (allowedAccounts, callContext) <- BalanceNewStyle.getAccountAccessAtBank(u, bankId, callContext) msg = s"$CannotFindAccountAccess AccountId(${accountId.value})" bankIdAccountId <- NewStyle.function.tryons(msg, 400, cc.callContext) { allowedAccounts.find(_.accountId==accountId).get } - (accountBalances, callContext)<- NewStyle.function.getBankAccountBalances(bankIdAccountId, callContext) + (accountBalances, callContext)<- BalanceNewStyle.getBankAccountBalances(bankIdAccountId, callContext) } yield { (createAccountBalancesJson(accountBalances), HttpCode.`200`(callContext)) } diff --git a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala index c155e64ce..ec6f86e00 100644 --- a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala +++ b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala @@ -12,6 +12,7 @@ import code.api.util.JwtUtil.{getSignedPayloadAsJson, verifyJwt} import code.api.util.NewStyle.HttpCode import code.api.util.X509.{getCommonName, getEmailAddress, getOrganization} import code.api.util._ +import code.api.util.newstyle.BalanceNewStyle import code.api.util.newstyle.Consumer.createConsumerNewStyle import code.api.util.newstyle.RegulatedEntityNewStyle.{createRegulatedEntityNewStyle, deleteRegulatedEntityNewStyle, getRegulatedEntitiesNewStyle, getRegulatedEntityByEntityIdNewStyle} import code.api.v2_1_0.{ConsumerRedirectUrlJSON, JSONFactory210} @@ -2186,7 +2187,7 @@ trait APIMethods510 { _ <- Helper.booleanToFuture(failMsg, 403, cc = callContext) { view.canSeeBankAccountBalance } - (accountBalances, callContext) <- NewStyle.function.getBankAccountBalances(bankIdAccountId, callContext) + (accountBalances, callContext) <- BalanceNewStyle.getBankAccountBalances(bankIdAccountId, callContext) } yield { (createAccountBalancesJson(accountBalances), HttpCode.`200`(callContext)) } @@ -2216,13 +2217,8 @@ trait APIMethods510 { cc => implicit val ec = EndpointContext(Some(cc)) for { (Full(u), callContext) <- SS.user - // Get all account accesses at the bank for the user - (views, accountAccesses) = Views.views.vend.privateViewsUserCanAccessAtBank(u, bankId) - // Filter views which can read the balance - canSeeBankAccountBalanceViews = views.filter(_.canSeeBankAccountBalance) - // Filter accounts the user has permission to see balances and remove duplicates - allowedAccounts = intersectAccountAccessAndView(accountAccesses, canSeeBankAccountBalanceViews) - (accountsBalances, callContext) <- NewStyle.function.getBankAccountsBalances(allowedAccounts, callContext) + (allowedAccounts, callContext) <- BalanceNewStyle.getAccountAccessAtBank(u, bankId, callContext) + (accountsBalances, callContext) <- BalanceNewStyle.getBankAccountsBalances(allowedAccounts, callContext) } yield { (createBalancesJson(accountsBalances), HttpCode.`200`(callContext)) } @@ -2252,16 +2248,8 @@ trait APIMethods510 { cc => implicit val ec = EndpointContext(Some(cc)) for { (Full(u), callContext) <- SS.user - // Get all account accesses at the bank for the user - (views, accountAccesses) = Views.views.vend.getAccountAccessAtBankThroughView(u, bankId, viewId) - _ <- Helper.booleanToFuture(CannotFindAccountAccess, 403, cc = callContext) { - views.nonEmpty - } - // Filter views which can read the balance - canSeeBankAccountBalanceViews = views.filter(_.canSeeBankAccountBalance) - // Filter accounts the user has permission to see balances and remove duplicates - allowedAccounts = APIUtil.intersectAccountAccessAndView(accountAccesses, canSeeBankAccountBalanceViews) - (accountsBalances, callContext) <- NewStyle.function.getBankAccountsBalances(allowedAccounts, callContext) + (allowedAccounts, callContext) <- BalanceNewStyle.getAccountAccessAtBankThroughView(u, bankId, viewId, callContext) + (accountsBalances, callContext) <- BalanceNewStyle.getBankAccountsBalances(allowedAccounts, callContext) } yield { (createBalancesJson(accountsBalances), HttpCode.`200`(callContext)) } diff --git a/obp-api/src/main/scala/code/remotedata/RemotedataViews.scala b/obp-api/src/main/scala/code/remotedata/RemotedataViews.scala index 9400bf194..a969510fd 100644 --- a/obp-api/src/main/scala/code/remotedata/RemotedataViews.scala +++ b/obp-api/src/main/scala/code/remotedata/RemotedataViews.scala @@ -119,8 +119,8 @@ object RemotedataViews extends ObpActorInit with Views { def privateViewsUserCanAccessAtBank(user: User, bankId: BankId): (List[View], List[AccountAccess]) = getValueFromFuture( (actor ? cc.privateViewsUserCanAccessAtBank(user, bankId)).mapTo[(List[View], List[AccountAccess])] ) - def getAccountAccessAtBankThroughViews(user: User, bankId: BankId, viewIds: List[ViewId]): ((List[View], List[AccountAccess])) = getValueFromFuture( - (actor ? cc.privateViewsUserCanAccessAtBankThroughViews(user, bankId, viewIds)).mapTo[(List[View], List[AccountAccess])] + def getAccountAccessAtBankThroughView(user: User, bankId: BankId, viewId: ViewId): ((List[View], List[AccountAccess])) = getValueFromFuture( + (actor ? cc.privateViewsUserCanAccessAtBankThroughView(user, bankId, viewId)).mapTo[(List[View], List[AccountAccess])] ) def privateViewsUserCanAccessForAccount(user: User, bankIdAccountId : BankIdAccountId): List[View] = getValueFromFuture( diff --git a/obp-api/src/main/scala/code/views/MapperViews.scala b/obp-api/src/main/scala/code/views/MapperViews.scala index 2f03028c2..a1ea6b5b3 100644 --- a/obp-api/src/main/scala/code/views/MapperViews.scala +++ b/obp-api/src/main/scala/code/views/MapperViews.scala @@ -584,11 +584,11 @@ object MapperViews extends Views with MdcLoggable { }) PrivateViewsUserCanAccessCommon(accountAccess) } - def getAccountAccessAtBankThroughViews(user: User, bankId: BankId, viewIds: List[ViewId]): (List[View], List[AccountAccess]) ={ + def getAccountAccessAtBankThroughView(user: User, bankId: BankId, viewId: ViewId): (List[View], List[AccountAccess]) ={ val accountAccess = AccountAccess.findAll( By(AccountAccess.user_fk, user.userPrimaryKey.value), By(AccountAccess.bank_id, bankId.value), - ByList(AccountAccess.view_id, viewIds.map(_.value)) + By(AccountAccess.view_id, viewId.value) ).filter(accountAccess => { val view = getViewFromAccountAccess(accountAccess) view.isDefined && view.map(_.isPrivate) == Full(true) diff --git a/obp-api/src/main/scala/code/views/Views.scala b/obp-api/src/main/scala/code/views/Views.scala index 362c3289d..6934281c9 100644 --- a/obp-api/src/main/scala/code/views/Views.scala +++ b/obp-api/src/main/scala/code/views/Views.scala @@ -84,9 +84,7 @@ trait Views { def privateViewsUserCanAccess(user: User): (List[View], List[AccountAccess]) def privateViewsUserCanAccess(user: User, viewIds: List[ViewId]): (List[View], List[AccountAccess]) def privateViewsUserCanAccessAtBank(user: User, bankId: BankId): (List[View], List[AccountAccess]) - def getAccountAccessAtBankThroughView(user: User, bankId: BankId, viewId: ViewId): (List[View], List[AccountAccess]) = - getAccountAccessAtBankThroughViews(user, bankId, List(viewId)) - def getAccountAccessAtBankThroughViews(user: User, bankId: BankId, viewIds: List[ViewId]): (List[View], List[AccountAccess]) + def getAccountAccessAtBankThroughView(user: User, bankId: BankId, viewId: ViewId): (List[View], List[AccountAccess]) def privateViewsUserCanAccessForAccount(user: User, bankIdAccountId : BankIdAccountId) : List[View] //the following return list[BankIdAccountId], just use the list[View] method, the View object contains enough data for it. @@ -153,7 +151,7 @@ class RemotedataViewsCaseClasses { case class privateViewsUserCanAccess(user: User) case class privateViewsUserCanAccessViaViewId(user: User, viewIds: List[ViewId]) case class privateViewsUserCanAccessAtBank(user: User, bankId: BankId) - case class privateViewsUserCanAccessAtBankThroughViews(user: User, bankId: BankId, viewIds: List[ViewId]) + case class privateViewsUserCanAccessAtBankThroughView(user: User, bankId: BankId, viewId: ViewId) case class privateViewsUserCanAccessForAccount(user: User, bankIdAccountId : BankIdAccountId) case class getAllFirehoseAccounts(bank: Bank, user : User) case class publicViews() From fe08c36a9b7df8a56137eaaba13f653b59acd84b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Fri, 24 May 2024 17:08:20 +0200 Subject: [PATCH 22/75] feature/View permissions WIP --- .../code/views/system/ViewPermission.scala | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 obp-api/src/main/scala/code/views/system/ViewPermission.scala diff --git a/obp-api/src/main/scala/code/views/system/ViewPermission.scala b/obp-api/src/main/scala/code/views/system/ViewPermission.scala new file mode 100644 index 000000000..ac4f8dbf5 --- /dev/null +++ b/obp-api/src/main/scala/code/views/system/ViewPermission.scala @@ -0,0 +1,23 @@ +package code.views.system + +import code.util.UUIDString +import com.openbankproject.commons.model._ +import net.liftweb.mapper._ +class ViewPermission extends LongKeyedMapper[ViewPermission] with IdPK with CreatedUpdated { + def getSingleton = ViewPermission + object bank_id extends MappedString(this, 255) + object account_id extends MappedString(this, 255) + object view_id extends UUIDString(this) + object permission extends MappedString(this, 255) +} +object ViewPermission extends ViewPermission with LongKeyedMetaMapper[ViewPermission] { + override def dbIndexes: List[BaseIndex[ViewPermission]] = UniqueIndex(bank_id, account_id, view_id, permission) :: super.dbIndexes + + // Very working progress + def findViewPermissions(bankId: BankId, accountId: AccountId, viewId: ViewId): List[ViewPermission] = + ViewPermission.findAll( + By(ViewPermission.bank_id, bankId.value), + By(ViewPermission.account_id, accountId.value), + By(ViewPermission.view_id, viewId.value) + ) +} From 3c9b34ca8523596630cf4449406ad8f82ff159c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Mon, 27 May 2024 18:37:09 +0200 Subject: [PATCH 23/75] feature/View permissions WIP 2 --- .../main/scala/bootstrap/liftweb/Boot.scala | 6 +- .../main/scala/code/views/MapperViews.scala | 136 +++++++++++++++++- .../code/views/system/ViewPermission.scala | 6 + 3 files changed, 143 insertions(+), 5 deletions(-) diff --git a/obp-api/src/main/scala/bootstrap/liftweb/Boot.scala b/obp-api/src/main/scala/bootstrap/liftweb/Boot.scala index 753f26f4b..765324708 100644 --- a/obp-api/src/main/scala/bootstrap/liftweb/Boot.scala +++ b/obp-api/src/main/scala/bootstrap/liftweb/Boot.scala @@ -129,7 +129,7 @@ import code.util.Helper.{MdcLoggable, ObpS, SILENCE_IS_GOLDEN} import code.util.{Helper, HydraUtil} import code.validation.JsonSchemaValidation import code.views.Views -import code.views.system.{AccountAccess, ViewDefinition} +import code.views.system.{AccountAccess, ViewDefinition, ViewPermission} import code.webhook.{BankAccountNotificationWebhook, MappedAccountWebhook, SystemAccountNotificationWebhook} import code.webuiprops.WebUiProps import com.openbankproject.commons.model.ErrorMessage @@ -143,7 +143,7 @@ import net.liftweb.http.LiftRules.DispatchPF import net.liftweb.http._ import net.liftweb.http.provider.HTTPCookie import net.liftweb.json.Extraction -import net.liftweb.mapper.{DefaultConnectionIdentifier=>_, _} +import net.liftweb.mapper.{DefaultConnectionIdentifier => _, _} import net.liftweb.sitemap.Loc._ import net.liftweb.sitemap._ import net.liftweb.util.Helpers._ @@ -273,6 +273,7 @@ class Boot extends MdcLoggable { if (APIUtil.getPropsAsBoolValue("create_system_views_at_boot", true)) { // Create system views + org.scalameta.logger.elem("I am creating system views") val owner = Views.views.vend.getOrCreateSystemView(SYSTEM_OWNER_VIEW_ID).isDefined val auditor = Views.views.vend.getOrCreateSystemView(SYSTEM_AUDITOR_VIEW_ID).isDefined val accountant = Views.views.vend.getOrCreateSystemView(SYSTEM_ACCOUNTANT_VIEW_ID).isDefined @@ -1086,6 +1087,7 @@ object ToSchemify { DynamicMessageDoc, EndpointTag, ProductFee, + ViewPermission, UserInitAction ) diff --git a/obp-api/src/main/scala/code/views/MapperViews.scala b/obp-api/src/main/scala/code/views/MapperViews.scala index a1ea6b5b3..1d09e3f75 100644 --- a/obp-api/src/main/scala/code/views/MapperViews.scala +++ b/obp-api/src/main/scala/code/views/MapperViews.scala @@ -9,7 +9,7 @@ import code.api.util.APIUtil._ import code.api.util.ErrorMessages._ import code.util.Helper.MdcLoggable import code.views.system.ViewDefinition.create -import code.views.system.{AccountAccess, ViewDefinition} +import code.views.system.{AccountAccess, ViewDefinition, ViewPermission} import com.openbankproject.commons.model.{UpdateViewJSON, _} import net.liftweb.common._ import net.liftweb.mapper.{Ascending, By, ByList, NullRef, OrderBy, PreCache, Schemifier} @@ -648,11 +648,141 @@ object MapperViews extends Views with MdcLoggable { theView } + +// private def migrateViewPermissions(view: View) = { +// ViewPermission.findViewPermissions(view.viewId).find(_.permission.get == "canSeeBankAccountBalance") match { +// case Some(permission) if !view.canSeeBankAccountBalance => +// ViewPermission.delete_!(permission) +// case Some(permission) if view.canSeeBankAccountBalance => +// // View definition is in accordance with View permission +// case _ => ViewPermission.create +// .bank_id(null) +// .account_id(null) +// .view_id(view.viewId.value) +// .permission("canSeeBankAccountBalance") +// .save +// } +// } + + private def migrateViewPermissions(view: View): Unit = { + val permissionNames = List( + "canSeeTransactionOtherBankAccount", + "canSeeTransactionMetadata", + "canSeeTransactionDescription", + "canSeeTransactionAmount", + "canSeeTransactionType", + "canSeeTransactionCurrency", + "canSeeTransactionStartDate", + "canSeeTransactionFinishDate", + "canSeeTransactionBalance", + "canSeeComments", + "canSeeOwnerComment", + "canSeeTags", + "canSeeImages", + "canSeeBankAccountOwners", + "canSeeBankAccountType", + "canSeeBankAccountBalance", + "canQueryAvailableFunds", + "canSeeBankAccountLabel", + "canSeeBankAccountNationalIdentifier", + "canSeeBankAccountSwift_bic", + "canSeeBankAccountIban", + "canSeeBankAccountNumber", + "canSeeBankAccountBankName", + "canSeeBankAccountBankPermalink", + "canSeeBankRoutingScheme", + "canSeeBankRoutingAddress", + "canSeeBankAccountRoutingScheme", + "canSeeBankAccountRoutingAddress", + "canSeeOtherAccountNationalIdentifier", + "canSeeOtherAccountSWIFT_BIC", + "canSeeOtherAccountIBAN", + "canSeeOtherAccountBankName", + "canSeeOtherAccountNumber", + "canSeeOtherAccountMetadata", + "canSeeOtherAccountKind", + "canSeeOtherBankRoutingScheme", + "canSeeOtherBankRoutingAddress", + "canSeeOtherAccountRoutingScheme", + "canSeeOtherAccountRoutingAddress", + "canSeeMoreInfo", + "canSeeUrl", + "canSeeImageUrl", + "canSeeOpenCorporatesUrl", + "canSeeCorporateLocation", + "canSeePhysicalLocation", + "canSeePublicAlias", + "canSeePrivateAlias", + "canAddMoreInfo", + "canAddURL", + "canAddImageURL", + "canAddOpenCorporatesUrl", + "canAddCorporateLocation", + "canAddPhysicalLocation", + "canAddPublicAlias", + "canAddPrivateAlias", + "canAddCounterparty", + "canGetCounterparty", + "canDeleteCounterparty", + "canDeleteCorporateLocation", + "canDeletePhysicalLocation", + "canEditOwnerComment", + "canAddComment", + "canDeleteComment", + "canAddTag", + "canDeleteTag", + "canAddImage", + "canDeleteImage", + "canAddWhereTag", + "canSeeWhereTag", + "canDeleteWhereTag", + "canAddTransactionRequestToOwnAccount", + "canAddTransactionRequestToAnyAccount", + "canSeeBankAccountCreditLimit", + "canCreateDirectDebit", + "canCreateStandingOrder", + "canRevokeAccessToCustomViews", + "canGrantAccessToCustomViews", + "canSeeTransactionRequests", + "canSeeTransactionRequestTypes", + "canSeeAvailableViewsForBankAccount", + "canUpdateBankAccountLabel", + "canCreateCustomView", + "canDeleteCustomView", + "canUpdateCustomView", + "canSeeViewsWithPermissionsForAllUsers", + "canSeeViewsWithPermissionsForOneUser" + ) + + permissionNames.foreach { permissionName => + // Get permission value + val permissionValue = view.getClass.getMethod(permissionName).invoke(view).asInstanceOf[Boolean] + + ViewPermission.findViewPermissions(view.viewId).find(_.permission.get == permissionName) match { + case Some(permission) if !permissionValue => + ViewPermission.delete_!(permission) + case Some(permission) if permissionValue => + // View definition is in accordance with View permission + case _ => + ViewPermission.create + .bank_id(null) + .account_id(null) + .view_id(view.viewId.value) + .permission(permissionName) + .save + } + } + } def getOrCreateSystemView(viewId: String) : Box[View] = { getExistingSystemView(viewId) match { - case Empty => createDefaultSystemView(viewId) - case Full(v) => Full(v) + case Empty => + val view = createDefaultSystemView(viewId) + view.map(v => migrateViewPermissions(v)) + view + case Full(v) => + migrateViewPermissions(v) + Full(v) case Failure(msg, t, c) => Failure(msg, t, c) case ParamFailure(x,y,z,q) => ParamFailure(x,y,z,q) } diff --git a/obp-api/src/main/scala/code/views/system/ViewPermission.scala b/obp-api/src/main/scala/code/views/system/ViewPermission.scala index ac4f8dbf5..5d3b11c10 100644 --- a/obp-api/src/main/scala/code/views/system/ViewPermission.scala +++ b/obp-api/src/main/scala/code/views/system/ViewPermission.scala @@ -19,5 +19,11 @@ object ViewPermission extends ViewPermission with LongKeyedMetaMapper[ViewPermis By(ViewPermission.bank_id, bankId.value), By(ViewPermission.account_id, accountId.value), By(ViewPermission.view_id, viewId.value) + ) // Very working progress + def findViewPermissions(viewId: ViewId): List[ViewPermission] = + ViewPermission.findAll( + NullRef(ViewPermission.bank_id), + NullRef(ViewPermission.account_id), + By(ViewPermission.view_id, viewId.value) ) } From 2ccbeb55a9c7e5087d8493587ca40d94bcba6306 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Tue, 28 May 2024 10:07:52 +0200 Subject: [PATCH 24/75] feature/improve the getMessageDocs endpoint --- .../main/scala/code/api/util/NewStyle.scala | 2 +- .../scala/code/bankconnectors/Connector.scala | 23 ++++++++++--------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/obp-api/src/main/scala/code/api/util/NewStyle.scala b/obp-api/src/main/scala/code/api/util/NewStyle.scala index f149bea63..5ed545881 100644 --- a/obp-api/src/main/scala/code/api/util/NewStyle.scala +++ b/obp-api/src/main/scala/code/api/util/NewStyle.scala @@ -3460,7 +3460,7 @@ object NewStyle extends MdcLoggable{ def getConnectorByName(connectorName: String): Option[Connector] = { if(supportedConnectorNames.exists(connectorName.startsWith _)) { - Connector.nameToConnector.get(connectorName).map(_()) + Connector.nameToConnector.get(connectorName) } else { None } diff --git a/obp-api/src/main/scala/code/bankconnectors/Connector.scala b/obp-api/src/main/scala/code/bankconnectors/Connector.scala index 077c481c6..52b42e346 100644 --- a/obp-api/src/main/scala/code/bankconnectors/Connector.scala +++ b/obp-api/src/main/scala/code/bankconnectors/Connector.scala @@ -78,24 +78,25 @@ Could consider a Map of ("resourceType" -> "provider") - this could tell us whic */ object Connector extends SimpleInjector { - - val nameToConnector: Map[String, () => Connector] = Map( - "mapped" -> lazyValue(LocalMappedConnector), - "akka_vDec2018" -> lazyValue(AkkaConnector_vDec2018), - "kafka_vSept2018" -> lazyValue(KafkaMappedConnector_vSept2018), - "kafka_vMay2019" -> lazyValue(KafkaMappedConnector_vMay2019), - "rest_vMar2019" -> lazyValue(RestConnector_vMar2019), - "stored_procedure_vDec2019" -> lazyValue(StoredProcedureConnector_vDec2019), + // An object is a class that has exactly one instance. It is created lazily when it is referenced, like a lazy val. + // As a top-level value, an object is a singleton. + // As a member of an enclosing class or as a local value, it behaves exactly like a lazy val. + val nameToConnector: Map[String, Connector] = Map( + "mapped" -> LocalMappedConnector, + "akka_vDec2018" -> AkkaConnector_vDec2018, + "kafka_vSept2018" -> KafkaMappedConnector_vSept2018, + "kafka_vMay2019" -> KafkaMappedConnector_vMay2019, + "rest_vMar2019" -> RestConnector_vMar2019, + "stored_procedure_vDec2019" -> StoredProcedureConnector_vDec2019, // this proxy connector only for unit test, can set connector=proxy in test.default.props, but never set it in default.props - "proxy" -> lazyValue(ConnectorUtils.proxyConnector), - "internal" -> lazyValue(InternalConnector.instance) + "proxy" -> ConnectorUtils.proxyConnector, + "internal" -> InternalConnector.instance ) def getConnectorInstance(connectorVersion: String): Connector = { connectorVersion match { case "star" => StarConnector case k => nameToConnector.get(k) - .map(f => f()) .getOrElse(throw new RuntimeException(s"Do not Support this connector version: $k")) } } From 5ec211cd34346b0c76f5c685e7e4791d74b78cd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Tue, 28 May 2024 11:26:11 +0200 Subject: [PATCH 25/75] feature/improve the getMessageDocs endpoint 2 --- obp-api/src/main/scala/code/api/util/ErrorMessages.scala | 2 +- obp-api/src/main/scala/code/api/v2_2_0/APIMethods220.scala | 4 ++-- obp-api/src/main/scala/code/bankconnectors/Connector.scala | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/obp-api/src/main/scala/code/api/util/ErrorMessages.scala b/obp-api/src/main/scala/code/api/util/ErrorMessages.scala index 500c7c97d..742e2843e 100644 --- a/obp-api/src/main/scala/code/api/util/ErrorMessages.scala +++ b/obp-api/src/main/scala/code/api/util/ErrorMessages.scala @@ -474,7 +474,7 @@ object ErrorMessages { val InsufficientAuthorisationToDeleteBranch = "OBP-30218: Insufficient authorisation to Create Branch. You do not have the role CanCreateBranch." // was OBP-20019 val InsufficientAuthorisationToCreateBank = "OBP-30210: Insufficient authorisation to Create Bank. You do not have the role CanCreateBank." // was OBP-20020 - val InvalidConnector = "OBP-30211: Invalid Connector Version. Please specify a valid value for CONNECTOR." + val InvalidConnector = "OBP-30211: Invalid Connector. Please specify a valid value for CONNECTOR." val EntitlementNotFound = "OBP-30212: EntitlementId not found" val UserDoesNotHaveEntitlement = "OBP-30213: USER_ID does not have the ENTITLEMENT_ID." diff --git a/obp-api/src/main/scala/code/api/v2_2_0/APIMethods220.scala b/obp-api/src/main/scala/code/api/v2_2_0/APIMethods220.scala index 2c811b04d..00531a24a 100644 --- a/obp-api/src/main/scala/code/api/v2_2_0/APIMethods220.scala +++ b/obp-api/src/main/scala/code/api/v2_2_0/APIMethods220.scala @@ -442,7 +442,7 @@ trait APIMethods220 { """.stripMargin, emptyObjectJson, messageDocsJson, - List(UnknownError), + List(InvalidConnector, UnknownError), List(apiTagDocumentation, apiTagApi) ) @@ -452,7 +452,7 @@ trait APIMethods220 { implicit val ec = EndpointContext(Some(cc)) for { connectorObject <- Future(tryo{Connector.getConnectorInstance(connector)}) map { i => - val msg = "$InvalidConnector Current Input is $connector. It should be eg: kafka_vSept2018..." + val msg = s"$InvalidConnector Current Input is $connector. It should be eg: kafka_vSept2018..." unboxFullOrFail(i, cc.callContext, msg) } } yield { diff --git a/obp-api/src/main/scala/code/bankconnectors/Connector.scala b/obp-api/src/main/scala/code/bankconnectors/Connector.scala index 52b42e346..d03e3b849 100644 --- a/obp-api/src/main/scala/code/bankconnectors/Connector.scala +++ b/obp-api/src/main/scala/code/bankconnectors/Connector.scala @@ -81,6 +81,7 @@ object Connector extends SimpleInjector { // An object is a class that has exactly one instance. It is created lazily when it is referenced, like a lazy val. // As a top-level value, an object is a singleton. // As a member of an enclosing class or as a local value, it behaves exactly like a lazy val. + // Previously the right hand part was surrounded by Functions.lazyValue function val nameToConnector: Map[String, Connector] = Map( "mapped" -> LocalMappedConnector, "akka_vDec2018" -> AkkaConnector_vDec2018, @@ -97,7 +98,7 @@ object Connector extends SimpleInjector { connectorVersion match { case "star" => StarConnector case k => nameToConnector.get(k) - .getOrElse(throw new RuntimeException(s"Do not Support this connector version: $k")) + .getOrElse(throw new RuntimeException(s"$InvalidConnector Current Input is $k")) } } From 5979f826ad9b8dbbea93185a92cdf3ac685097d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Tue, 28 May 2024 11:34:17 +0200 Subject: [PATCH 26/75] refactor/Remove commented out code --- .../src/main/scala/bootstrap/liftweb/Boot.scala | 1 - .../src/main/scala/code/views/MapperViews.scala | 15 --------------- 2 files changed, 16 deletions(-) diff --git a/obp-api/src/main/scala/bootstrap/liftweb/Boot.scala b/obp-api/src/main/scala/bootstrap/liftweb/Boot.scala index 765324708..676fc2985 100644 --- a/obp-api/src/main/scala/bootstrap/liftweb/Boot.scala +++ b/obp-api/src/main/scala/bootstrap/liftweb/Boot.scala @@ -273,7 +273,6 @@ class Boot extends MdcLoggable { if (APIUtil.getPropsAsBoolValue("create_system_views_at_boot", true)) { // Create system views - org.scalameta.logger.elem("I am creating system views") val owner = Views.views.vend.getOrCreateSystemView(SYSTEM_OWNER_VIEW_ID).isDefined val auditor = Views.views.vend.getOrCreateSystemView(SYSTEM_AUDITOR_VIEW_ID).isDefined val accountant = Views.views.vend.getOrCreateSystemView(SYSTEM_ACCOUNTANT_VIEW_ID).isDefined diff --git a/obp-api/src/main/scala/code/views/MapperViews.scala b/obp-api/src/main/scala/code/views/MapperViews.scala index 1d09e3f75..2119cb30e 100644 --- a/obp-api/src/main/scala/code/views/MapperViews.scala +++ b/obp-api/src/main/scala/code/views/MapperViews.scala @@ -649,21 +649,6 @@ object MapperViews extends Views with MdcLoggable { theView } -// private def migrateViewPermissions(view: View) = { -// ViewPermission.findViewPermissions(view.viewId).find(_.permission.get == "canSeeBankAccountBalance") match { -// case Some(permission) if !view.canSeeBankAccountBalance => -// ViewPermission.delete_!(permission) -// case Some(permission) if view.canSeeBankAccountBalance => -// // View definition is in accordance with View permission -// case _ => ViewPermission.create -// .bank_id(null) -// .account_id(null) -// .view_id(view.viewId.value) -// .permission("canSeeBankAccountBalance") -// .save -// } -// } - private def migrateViewPermissions(view: View): Unit = { val permissionNames = List( "canSeeTransactionOtherBankAccount", From 3ee8839ac20e07fa24f4a73097262c9a59f4b836 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Tue, 28 May 2024 14:47:12 +0200 Subject: [PATCH 27/75] feature/clean up webui_api_documentation_url code --- obp-api/src/main/scala/code/snippet/WebUI.scala | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/obp-api/src/main/scala/code/snippet/WebUI.scala b/obp-api/src/main/scala/code/snippet/WebUI.scala index 6ac8bddf1..90bfdc7d7 100644 --- a/obp-api/src/main/scala/code/snippet/WebUI.scala +++ b/obp-api/src/main/scala/code/snippet/WebUI.scala @@ -328,12 +328,7 @@ class WebUI extends MdcLoggable{ def sandboxIntroductionLink: CssSel = { val webUiApiDocumentation = getWebUiPropsValue("webui_api_documentation_url",s"${getServerUrl}/introduction") - val apiDocumentation = - if (webUiApiDocumentation == s"${getServerUrl}/introduction") - webUiApiDocumentation - else - webUiApiDocumentation + "#Sandbox-Introduction" - "#sandbox-introduction-link [href]" #> scala.xml.Unparsed(apiDocumentation) + "#sandbox-introduction-link [href]" #> scala.xml.Unparsed(webUiApiDocumentation) } def technicalFaqsAnchor: CssSel = { From 20edc9eaa574a2bf792e44b502a035105a1fa403 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Mon, 3 Jun 2024 09:41:59 +0200 Subject: [PATCH 28/75] feature/Renew Tesobe Certificates regarding MTLS mode --- obp-api/src/test/resources/cert/client.pfx | Bin 3668 -> 0 bytes .../test/resources/cert/localhost_SAN_dns_ip.pfx | Bin 2849 -> 0 bytes .../test/resources/cert/localhost_san_dns_ip.pfx | Bin 0 -> 2670 bytes 3 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 obp-api/src/test/resources/cert/client.pfx delete mode 100644 obp-api/src/test/resources/cert/localhost_SAN_dns_ip.pfx create mode 100644 obp-api/src/test/resources/cert/localhost_san_dns_ip.pfx diff --git a/obp-api/src/test/resources/cert/client.pfx b/obp-api/src/test/resources/cert/client.pfx deleted file mode 100644 index cb1159850c065679c297fe9bd74ffebd6fba0f44..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3668 zcmZXWXHXN0m&FqZ5D0`W<A)lfxxkluXs z(0h@ND1Ucmci+zJhdbxaId{&-J3km2YJdmChoPa2gv0_-8c|ndKq6p18VUiSp+tY| zKM^?d--s|D4UYL^Mc@Gef8^D_2@u9aNb=teq<Sp2M9VfsqE}ylsuyYo4|+c)`g@Y%^TIK?|75)B*S+=K zETq*W(K{QZ-w)#pRP-|gVx5R*FYVZrv;TU$=Do{%-ocM;qF40*#rF^bHf#E-ZP|eb;dJ-lsA1ei^n@@AuN^~ z7&H9q#;u|6aAv6+@V2{C(YObbVu0KF`BE1TU%GFqo8v{u_pC)-t)HT zT=Lf+r4n+(NsczVo=4Q1qep7JArj*@(h(0Zqa?!=Yg#gkU)LGQVpr3Cik*)x-nw!B zqE@FySamyV;BzfhNPX7yyj&=Dk7fI2K4DN(O}}CzC3q|9xy9y^V^8G#j4Ii>l#voO z;v=+J+SSKhpmfk4z9Pb46u<= zU>V zafjJ8+*}a_vyJg*>S1F1E!oZpnZNU_+J^kg@EX)I@59s_O@;?Zug%BfxQc#c!M^eI z`%jA>26TN>&~Y`&!N3A@?QmNg%wbgVgX2qfcZWjND|-r1R{AvG)BtPv7iE zN?Y7P8yu@!LFOh3ghnV(coDq8B&sr?)sJFbJJK7}BapyhAhWg4(wLQ4cTZ}wO{YTv z&tC5y7r)(`iU_!~{}>Xvz$(Dk%7AgVo$YL$%U(RvOqx@zP%^cLd@cL@qecE_p?;z7 z>U8gO1Ya(=Gs-`xqr{+4g~&KO%$3`dsyC#VmS1UGDTATvuI6SAjSo-tze`dYiP`{O zeM$`eTxm)r0ExemwGWvGdH4LS3b-}@;Zy#jwuV9Nc?Ve7rRrIn@#>9Q2o%R<5EKjLlR6kJMeLS?!&&ajcL5;ILM zo5Knc=Twj7EIE{727(r>aXzY+GQ*>o)2ZzZRNhtuURj--^z8Gsesn80eN>TvNVsBQ zj%Dbj(uQ2#;t;dNyR^5qm&?MD^2((HOxx+x7c9fQcS2tA+}iIFvcEH2T1xHEHBg`{ z77po#c>CT=SSv862Bt`0^Nd62BBZ6-;PH}@0z{;vEZ@`Ko*@bmKK^w4!S@!2YTex~ zpXB3hrCfzFICwBGTr=C}3Z||axK@*v28q>e9w$Ox=bgcuCfQ!YK%zA%w7bBiY0CRf z<_Pm&i4AJk!g%xz7T!<7S~pxc4R!IQ$K^;gQ**QV-jqn%tS!ymdQjW7QNpz3zuGWm zG2e*QYd!Q?a#JFX>tV|K`Nqk*Snyp>-9&oQO&;{RxMNs{2bJ3*B)sJ__#MU$_BNf% z?XED*#UHa}6Evd1X0kkxaiK>Y@ArLxrN-S22a zj>>chcV6*@YI`KL4+z1Dv-5+F)YR3M7MOzD5($LoNQwcBO)MOCEiA)^9orO^$<2!U z*s8fb&OW9+SgE;R&+3simUYyDqaYg$qq?9e;;er*Z~(~Vdv7eWA>n?zqA#7GvRdMt zO%+KPQkLAJh!FANIsZ;%wo+zaW#wv-{wOD%o<(szTzC0|NG*xfsui*2@WK6Q4NQdS z==W6#7h6thN2er_QOaewHL%CQ&6B$`(vR>9jbHPicnt&Q=Q@E!c#L<(V6;_OhxN_E z%BZ3GVSv8yga`=W{kayvq^ z&Puk>!FiY3ApeR4Lu~t9!ei_HV0D#wSYRX6w$hypn@7vB`sNq<;^~m7y4KVwQai=* zJT7Ly0x8C$r3ZK*(OldDQ4vQx@=bu6tgR2Ndm>Kw9;>3#ja#1P!iyyn zL~@EUGI-I!l&@~l@U19u!;?m^)~-5LvuFc~+S<-3c2Idh8HQG2g`o)?{ttlUqX|qv zXad7Ow(g(NB!v7Aj*tNH^3k9p7#g(q|0xApuA@4A3OxTWN&%rkgTE@R8D~%mBu|a} z^C>$`GbAM+YkRPXl~m1j=8&&Wd>?oLQ1vW9g$3x#>kEkOFW=z89({ac?M?hFV{<%` zL>K^xuK~I<1S0WJ&OHQQdtCTMgPmpyHqR+Y4g0(BY1?o?{4O-i-sD*lz6NqDjRPE7 zQ~iVD?A7aK;3;<5l4EiRR$c!6;JGPnMdwr;FF$h<=4=Op7-rB}aKX9^j&#?hSY5Ge`@+^rA z{PT7OXYYvLL7AV?e1HZsY^}_S!a>niU3RUhj1(^&6e2v3)6!@^^4;}54_;XrGcmJs z>Ljvi;U7nj*H$${ItZ~jioaTs+1YIh7^#!$gTDv=G04dAz1Tl)p~V<K%dkV1ZkJn-kQo%&z|Zkq3tQ2 z=KFS3c4bjdx_w|nre`b1?gZ}84jfFc-4d{4OlLy7mP0$7(xC*pAwPK;(w@FhStd(b z8E6ys;y==yWqH}UX052pn$!=<;V=`neN$y@3umlsFRN?*kcFdE=}u@QR&Hla)cmWm zLVc=K)0-tl*Ghgi^^?e!3Z%I3y{K+3Q(Ka8Wgu=e=q}|4X^vk!Kh#iVs(j(QV&oR6 zag%RjOGwS(q<9l2$(4a^ZhLhU7sf;Ha#F{h-nZyjqtEJ=-pHd@ySwt7ll^KeI1>7b zc}3Rv4A4rruSpJ<8hv)xqPxfcYJG5t@TRjrr$+oTEVdVN~{RgekxJ9B%-W1{kL1+=E)Ax z+HtoGt`POGAu+< z+}y`d_l|7sYFC23rSN-|p4LE(3EYT=Zlm$YT$lFu*-G}7Kei_r zIOu(GG=!#mm)&G6YSU{-NUPc{9CLJU*MAIn@?|QhsJ-SV|D*h$>p-WR0e1rMTKYfVO)TiJtE7ZqPyyTko&bA*-5*8%*?a*nV4VM+(26|( z(a3$1)u1!6%#9+VyqxWOEkd*{CIJ(MvBQW72?U||c#H%95Dgr*>1Kht42{H zty!mLOR1R0YO3vZKKI_!`^WF|{NCqz-{;RS0>>gs2c$>fAQKGC@Fe4;V>Tcokbr~q zfpC!SGy4?+2Ri;8F%WQ|^)u@;9T0FP^S=`yLWBYG-v-vRMs^Sr!T400u$x#)N5=$+ z#eo|3e}&4A^>uyQ_vVI%AdTWL9NsuwU>;MSwwBk7sK(Z#_3WmkE$*d#W2vg1FQf~p zaLLp<8YerQ@a%WYJ+P~tU0R=12_0FoiH~xsgdXUPzvQ(EJFJu7xt%>ne_U@+zpL1+ zeY9gAhK&k4_D$G z3D~7!&7vbJ?5B_kSW&+9yj0eFLJa5*gbWZumR=Uf+O=F6Py6nxe8pWR;zM||cT;^= zKI+*{OyEI^)cO|+u8BHX{^itJ*ltxkRQx*84MQUHPYSrMtq;!3)Dx2yNh3BrOzKy1 z(mj0B-=(NR`T3*X#HjRIxWj(fd{TVj_uP1{aO;z%fO~o_6$yRKZGH| zW&SwOXpZO7TX*=bOp<(8wp z?$Z%)04!?sk4MFxd+o1e2;v4}GCK=cDCqP@(<<)_*3&@34weFf&lkDoc9INin<^2K zU*DKMMPz=wH&eA7-+c8`k^UDGOGB#i5@>bfY1kB+&~AStog`JdF#z@7yzkD%MZV|6 zqvq1GX%*|7cK-A;>m24datO+`H0Ni_qcI#8m86++q%`y_dHlt@;>pWL}QOKB`+m2Szs$`zZ43 zu1@2niAgi3GKyC<-$ivFEDk=J)lxG`Y8MMM$7aTF z@q1Nl*}QPg)0-MsqTUwxfHeWafvwcETaBD;<%8$e8#(9d3wkZFWAuML&X#JUzcz@e z+K>Fa=aIaMvUssEJi92^H?piX8_H{p&3Dt{_vOA-S`(~T&b;Q`znDpSEF#VjUIahh zHD{Zv{F>y~0flu)4K!3^1RMB{2CO-}S308sqsmt zuvOo7Lp>XlSb5S?6sDGWfF`*&7QckYqs|| zatu}GJYv(|jplGazqCl#YwIBB-8$#a7E^L85#g=>I`hA}CaqeurXsJO2G0uhk3? z4n%Q20U?flc7EveC}h>9n&{-HDYchZGc!c2Aquu-t&r&Vtwom_{`Iw%F(02@|2EW0 z*FJB%qY#{}Xn!r=sa+$YJ?y@y$ja8It$DVEmC@D0=ncx9*QtG67Z#S=^L!0LIad~? zw0(of{;Z#}Gs!N-5)x`z8#cIpU3-XPyb<@{)8*DgVEU&!5vVX62hV#)Ph_k>Qw9B`_b$H3220fwy+hVhHp#G{OBLPF*O#YQ3BOdB8Z}*{LFEcOGQvMX zc+bNZ&8;RMX;)Y^vPXux$|Rm%_3`Lvj?J4K6(Nh+k-ldy-xOb>&zsLpGB|Rf645T< zocf!|Ya@}6qKq6Fs7ELP{Du$9+oTbb2W>w-}+m)vmsAL7W(60_T3tt3MR2SZ!m9EhdxA&iz~Bstg{lUZG>DlOgG;G@J8 z!_-N9_^vqoUx(~Hk*$8k)*3q%gZV#EpRECnP7dFMpr&D*^7e1bqzvv)RbHB^{9^ot zls%s2PRgrbpF7H)8hEY^i}P5}xw#Y!7t&F{E@;?y&#%ImHZO7>>KuhHJ&d7VQnOsDqZvfF%c311h^X)JZ{^e-aApy&OX$S3_`+X%F2F5_C5F0}4k z+0uU0qd*qzhRk-iN^WS~2o@UjNDkh$ap9X7#myR3Vz9w#yxurpH3VdIJLJc(5ikJK zfqb9~o1eFwAF`H07=er^cwrNt2y$Sb8wpJpAoG31mDU(N4+_elX3Igel?);bvmFPl zZ?#{@&4cwFXekvXKp(pB5ky-4)H;cxonAE{OC0@R6Ah$q(X{*)2^c zaAkO}rlpN+`>I*M;$0dL>c5wGk#I3tfFIyC;3nY4nR=i7LI7BR1i%KM3ot)ZcR;|I zBLVOM1R``1NCewISfvAk0U(L*gj@y%+eX_moXDN8t?kZ35qJ!IQ(o0ysk9CC(YXo4 QWVB=Vim^*@=5I#-8~O@1VE_OC diff --git a/obp-api/src/test/resources/cert/localhost_san_dns_ip.pfx b/obp-api/src/test/resources/cert/localhost_san_dns_ip.pfx new file mode 100644 index 0000000000000000000000000000000000000000..4f1cf33444cf38399a7955e282b0de0a95e38954 GIT binary patch literal 2670 zcmZXUc{CJ^8pdas8B5GG*0HbImr=+TL&DgJ$i9uSRR(G7Oj#llB}IiH`x@bk!U6Yk+fI@Joq;JSHYq zKq3Kz+N%O+h$<{ZnZPb>qP@%u+jbxP+&J&9#bHsO6J8h+ETKq#t2mq^#NA27Dzf^t zNM1_2r0#mHM6*!oVUR2Fxa^L0!nyE8F203R%=?+zDKY03N7)0!-i@4^E&di^RlmQd zdVSz!Jlb`le9RjPmI>N`wdPJq-pe|ZcAo8Y8@joI46}e;>pIE0g9~n7&k6lLelbt= zF9VG|gRLHsz5KS@E+?#z?Z*(-XdO z%nOf@Ct=Edi7B^s_dialQ=j+j^=lG%eS6m6NSH~7jw+_E*ZH?|Yk#DKFhdS|z7)&v zUm^9N+bYh`8)c6MuAnrqRxa$oyHKy$J7zIHB9hUXhp{OyqgU7X$Sb&u2HKwLv?CS2 z)r(LaQ7l)f?obyDQjPxz(Q=UE^9)q2$RIf-Cky=!!J#L3^Hi#4NfcK- zOu#sA(`wKPhI<#(X$APP3FT%&2W2Qp9Jb1rzvSR{!Ww$YK|)B@HzLG|WA=(^JvZ;U znxf!G>vE)Y9_#7{n`E5NAECp$YRCEljuczkUq}bN2G*2Nt?SgqLfKQuzfo z*a9ue?R#DGP)lRhW3Rs2NIhoG2SYdYLiES6o;16iM1}W0+a1&NN@JY#iFuK$sXA~f zM`GO4z3@&%3q_!xs@Kil(X}n;DI;$m|8sY+K`?@JMMllud(-sKceNLrK9^gJh1yau z5i425NcfqtH0&a0s-+;5N0hDDo9YX3HHWsG5?>uTirtxW?8(3N%=NsJ3 zoKi(iclc=Ez%A8(iX8l1+FGH(J%MSS+T1m-vV$YQauFg^;o21j9x6{`NMdZtiub-~ z5$9$-{PLDkYfp>L&Q6-Hn^F;X%}-8g>^N4nPkSx6p0RhN>ZFh0-A4Equcu-CO=YJ2 zY>e2Xt?78piW4a3&Cj!`FiPd@L}icqIBZK^urRVUJ?g99TB<#e%G8D8Pn`7|?u@KP z62Q^_1DqlPI2c3#V;Qk8V>}_J{(~QOAQOXA-x+|S{y&w#!C}_tVkkm?Qwc->eV{74 z$Dy{%`E}EMEAJeA#nzkdWln$dSi3GBy20F&IDa}&wcEH4=G|&CgSrJOb9mo**};b% z=ti~%t}rynUJeesd?%&JQVXTc>LBR`To8)`;zP)c8(Wz1UKGNIo19SJgWu@3!cslVC?%2l2Pg;ezBm?1 zjZ!Z$SbQ;AnUH+~sXRX0X!Sy*bTycy3ggDR}lr07_5j$jug4$9>N zY2*A)^WjG+fy4=E=sM0%#%pX37KdtS`efy!sue>W<+*k~G25`3sgiW%&p0$YxO<4c{#8_yv_f1o_`~kZN4myYuiC&Q}(@@us~i zoHqbGU*>;^@c+G|mw4AzA||{PO>x0>ALg%k77bC1*B{BT$#q=A+Y9lP-%ph=AwM>h z^nO<8(3>MGu=Y}er6H<w-eB56!fpH@ED=dSOx8QX-KMwJb} zX_gIYdolq0(R;5}odwc0G9f9rgr}j1vCm;~Pas@xhP#dPz>1hC`6nlO=F5fGdpWXC zJZkKpi@CpwF12aVR?b$^`)-4=XR+T=S$upjVUHP@!z8)eOOFQ#uOQxjGhsjAW253< zU0>r1)p?^^;4JR>{xAB+J65n)Y&Vf5RqD0D#02it@Ybd34gCa2VVf(>Q`$0paC)h1 z#!`HH_!(i@q@e!MS&Qku$br%>mInP5xmCHZjeH!D7TvF!g^jQwqGmlJBb9!eTuS9< z1#UmP*`0E*8Qx1b=rI(iFJP%UA6*c1wSd8L>E zG0Y$)AQ%7=&o7COX&X7%i}*Nzx>u=5T^2i*#Md2oaK6JSOjOMjgIM1M%6c}@pC|r} G+kXMMz~ATq literal 0 HcmV?d00001 From 3bd320860873e27b2241055c838a8f2f420b3066 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Tue, 4 Jun 2024 14:12:30 +0200 Subject: [PATCH 29/75] bugfix/Fix the function createCoreAccountsByCoreAccountsJSON --- obp-api/src/main/scala/code/api/v3_0_0/APIMethods300.scala | 4 ++-- obp-api/src/main/scala/code/api/v3_0_0/JSONFactory3.0.0.scala | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/obp-api/src/main/scala/code/api/v3_0_0/APIMethods300.scala b/obp-api/src/main/scala/code/api/v3_0_0/APIMethods300.scala index 992c76ed3..a939fd855 100644 --- a/obp-api/src/main/scala/code/api/v3_0_0/APIMethods300.scala +++ b/obp-api/src/main/scala/code/api/v3_0_0/APIMethods300.scala @@ -500,7 +500,7 @@ trait APIMethods300 { availablePrivateAccounts <- Views.views.vend.getPrivateBankAccountsFuture(u) (coreAccounts, callContext) <- getFilteredCoreAccounts(availablePrivateAccounts, req, callContext) } yield { - (JSONFactory300.createCoreAccountsByCoreAccountsJSON(coreAccounts), HttpCode.`200`(callContext)) + (JSONFactory300.createCoreAccountsByCoreAccountsJSON(coreAccounts, u), HttpCode.`200`(callContext)) } } } @@ -1710,7 +1710,7 @@ trait APIMethods300 { availablePrivateAccounts <- Views.views.vend.getPrivateBankAccountsFuture(u, bankId) (accounts, callContext) <- getFilteredCoreAccounts(availablePrivateAccounts, req, callContext) } yield { - (JSONFactory300.createCoreAccountsByCoreAccountsJSON(accounts), HttpCode.`200`(callContext)) + (JSONFactory300.createCoreAccountsByCoreAccountsJSON(accounts, u), HttpCode.`200`(callContext)) } } } diff --git a/obp-api/src/main/scala/code/api/v3_0_0/JSONFactory3.0.0.scala b/obp-api/src/main/scala/code/api/v3_0_0/JSONFactory3.0.0.scala index 20cefeceb..3effc4857 100644 --- a/obp-api/src/main/scala/code/api/v3_0_0/JSONFactory3.0.0.scala +++ b/obp-api/src/main/scala/code/api/v3_0_0/JSONFactory3.0.0.scala @@ -846,7 +846,7 @@ object JSONFactory300{ ) ) - def createCoreAccountsByCoreAccountsJSON(coreAccounts : List[CoreAccount]): CoreAccountsJsonV300 = + def createCoreAccountsByCoreAccountsJSON(coreAccounts : List[CoreAccount], user: User): CoreAccountsJsonV300 = CoreAccountsJsonV300(coreAccounts.map(coreAccount => CoreAccountJson( coreAccount.id, coreAccount.label, @@ -854,7 +854,7 @@ object JSONFactory300{ coreAccount.accountType, coreAccount.accountRoutings.map(accountRounting =>AccountRoutingJsonV121(accountRounting.scheme, accountRounting.address)), views = Views.views.vend - .assignedViewsForAccount(BankIdAccountId(BankId(coreAccount.bankId), AccountId(coreAccount.id))).filter(_.isPrivate) + .privateViewsUserCanAccessForAccount(user, BankIdAccountId(BankId(coreAccount.bankId), AccountId(coreAccount.id))).filter(_.isPrivate) .map(mappedView => ViewBasicV300( mappedView.viewId.value, From e5e8176793adff3e656139b5ed1ee88688dbf6e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Tue, 4 Jun 2024 15:54:44 +0200 Subject: [PATCH 30/75] feature/Add endpoint getCoreAccountByIdThroughView v5.1.0 --- .../scala/code/api/v5_1_0/APIMethods510.scala | 33 +++++++++++++++++- .../code/api/v5_1_0/AccountAccessTest.scala | 34 +++++++++++++++++-- .../scala/code/api/v5_1_0/AccountTest.scala | 21 +++--------- 3 files changed, 68 insertions(+), 20 deletions(-) diff --git a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala index ec6f86e00..b04786342 100644 --- a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala +++ b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala @@ -20,7 +20,7 @@ import code.api.v3_0_0.JSONFactory300 import code.api.v3_0_0.JSONFactory300.createAggregateMetricJson import code.api.v3_1_0.ConsentJsonV310 import code.api.v3_1_0.JSONFactory310.createBadLoginStatusJson -import code.api.v4_0_0.JSONFactory400.{createAccountBalancesJson, createBalancesJson} +import code.api.v4_0_0.JSONFactory400.{createAccountBalancesJson, createBalancesJson, createNewCoreBankAccountJson} import code.api.v4_0_0.{JSONFactory400, PostAccountAccessJsonV400, PostApiCollectionJson400, RevokedJsonV400} import code.api.v5_1_0.JSONFactory510.{createRegulatedEntitiesJson, createRegulatedEntityJson} import code.atmattribute.AtmAttribute @@ -2155,6 +2155,37 @@ trait APIMethods510 { } } + + + staticResourceDocs += ResourceDoc( + getCoreAccountByIdThroughView, + implementedInApiVersion, + nameOf(getCoreAccountByIdThroughView), + "GET", + "/banks/BANK_ID/accounts/ACCOUNT_ID/views/VIEW_ID", + "Get Account by Id (Core) through the VIEW_ID", + s"""Information returned about the account through VIEW_ID : + |""".stripMargin, + EmptyBody, + moderatedCoreAccountJsonV400, + List($UserNotLoggedIn, $BankAccountNotFound,UnknownError), + apiTagAccount :: apiTagPSD2AIS :: apiTagPsd2 :: Nil + ) + lazy val getCoreAccountByIdThroughView : OBPEndpoint = { + case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: "views" :: ViewId(viewId) :: Nil JsonGet req => { + cc => implicit val ec = EndpointContext(Some(cc)) + for { + (user @Full(u), account, callContext) <- SS.userAccount + bankIdAccountId = BankIdAccountId(account.bankId, account.accountId) + view <- NewStyle.function.checkViewAccessAndReturnView(viewId , bankIdAccountId, user, callContext) + moderatedAccount <- NewStyle.function.moderatedBankAccountCore(account, view, user, callContext) + } yield { + val availableViews: List[View] = Views.views.vend.privateViewsUserCanAccessForAccount(u, BankIdAccountId(account.bankId, account.accountId)) + (createNewCoreBankAccountJson(moderatedAccount, availableViews), HttpCode.`200`(callContext)) + } + } + } + staticResourceDocs += ResourceDoc( getBankAccountBalances, implementedInApiVersion, diff --git a/obp-api/src/test/scala/code/api/v5_1_0/AccountAccessTest.scala b/obp-api/src/test/scala/code/api/v5_1_0/AccountAccessTest.scala index 98924bb88..f12d30b09 100644 --- a/obp-api/src/test/scala/code/api/v5_1_0/AccountAccessTest.scala +++ b/obp-api/src/test/scala/code/api/v5_1_0/AccountAccessTest.scala @@ -5,10 +5,11 @@ import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON.createViewJsonV300 import code.api.util.APIUtil.OAuth._ import code.api.util.ApiRole -import code.api.util.ErrorMessages.{UserLacksPermissionCanGrantAccessToCustomViewForTargetAccount, UserLacksPermissionCanGrantAccessToSystemViewForTargetAccount, UserLacksPermissionCanGrantAccessToViewForTargetAccount, UserLacksPermissionCanRevokeAccessToCustomViewForTargetAccount, UserLacksPermissionCanRevokeAccessToSystemViewForTargetAccount, UserLacksPermissionCanRevokeAccessToViewForTargetAccount, UserNotLoggedIn} +import code.api.util.ApiRole.CanSeeAccountAccessForAnyUser +import code.api.util.ErrorMessages._ import code.api.v3_0_0.ViewJsonV300 import code.api.v3_1_0.CreateAccountResponseJsonV310 -import code.api.v4_0_0.RevokedJsonV400 +import code.api.v4_0_0.{AccountsMinimalJson400, RevokedJsonV400} import code.api.v5_1_0.OBPAPI5_1_0.Implementations5_1_0 import code.entitlement.Entitlement import com.github.dwickern.macros.NameOf.nameOf @@ -30,6 +31,7 @@ class AccountAccessTest extends V510ServerSetup { object ApiEndpoint1 extends Tag(nameOf(Implementations5_1_0.grantUserAccessToViewById)) object ApiEndpoint2 extends Tag(nameOf(Implementations5_1_0.revokeUserAccessToViewById)) object ApiEndpoint3 extends Tag(nameOf(Implementations5_1_0.createUserWithAccountAccessById)) + object GetAccountAccessByUserId extends Tag(nameOf(Implementations5_1_0.getAccountAccessByUserId)) lazy val bankId = randomBankId @@ -54,6 +56,34 @@ class AccountAccessTest extends V510ServerSetup { createViewViaEndpoint(bankId, accountId, postBodyViewJson, user1) } + + + feature(s"test ${GetAccountAccessByUserId.name}") { + scenario(s"We will test ${GetAccountAccessByUserId.name}", GetAccountAccessByUserId, VersionOfApi) { + + val requestGet = (v5_1_0_Request / "users" / resourceUser2.userId / "account-access").GET + + // Anonymous call fails + val anonymousResponseGet = makeGetRequest(requestGet) + anonymousResponseGet.code should equal(401) + anonymousResponseGet.body.extract[ErrorMessage].message should equal(UserNotLoggedIn) + + // Call endpoint without the entitlement + val badResponseGet = makeGetRequest(requestGet <@ user1) + badResponseGet.code should equal(403) + val errorMessage = badResponseGet.body.extract[ErrorMessage].message + errorMessage contains UserHasMissingRoles should be (true) + errorMessage contains CanSeeAccountAccessForAnyUser.toString() should be (true) + + // All good + Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanSeeAccountAccessForAnyUser.toString()) + val goodResponseGet = makeGetRequest(requestGet <@ user1) + goodResponseGet.code should equal(200) + goodResponseGet.body.extract[AccountsMinimalJson400] + + } + } + feature(s"test $ApiEndpoint1 Authorized access") { scenario("We will call the endpoint without user credentials", ApiEndpoint1, VersionOfApi) { diff --git a/obp-api/src/test/scala/code/api/v5_1_0/AccountTest.scala b/obp-api/src/test/scala/code/api/v5_1_0/AccountTest.scala index 131f61650..59cef9533 100644 --- a/obp-api/src/test/scala/code/api/v5_1_0/AccountTest.scala +++ b/obp-api/src/test/scala/code/api/v5_1_0/AccountTest.scala @@ -20,31 +20,18 @@ class AccountTest extends V510ServerSetup { * This is made possible by the scalatest maven plugin */ object VersionOfApi extends Tag(ApiVersion.v5_1_0.toString) - object GetAccountAccessByUserId extends Tag(nameOf(Implementations5_1_0.getAccountAccessByUserId)) + object GetCoreAccountByIdThroughView extends Tag(nameOf(Implementations5_1_0.getCoreAccountByIdThroughView)) - feature(s"test ${GetAccountAccessByUserId.name}") { - scenario(s"We will test ${GetAccountAccessByUserId.name}", GetAccountAccessByUserId, VersionOfApi) { + feature(s"test ${GetCoreAccountByIdThroughView.name}") { + scenario(s"We will test ${GetCoreAccountByIdThroughView.name}", GetCoreAccountByIdThroughView, VersionOfApi) { - val requestGet = (v5_1_0_Request / "users" / resourceUser2.userId / "account-access").GET + val requestGet = (v5_1_0_Request / "banks" / "BANK_ID" / "accounts" / "ACCOUNT_ID"/ "views" / "VIEW_ID").GET // Anonymous call fails val anonymousResponseGet = makeGetRequest(requestGet) anonymousResponseGet.code should equal(401) anonymousResponseGet.body.extract[ErrorMessage].message should equal(UserNotLoggedIn) - // Call endpoint without the entitlement - val badResponseGet = makeGetRequest(requestGet <@ user1) - badResponseGet.code should equal(403) - val errorMessage = badResponseGet.body.extract[ErrorMessage].message - errorMessage contains UserHasMissingRoles should be (true) - errorMessage contains CanSeeAccountAccessForAnyUser.toString() should be (true) - - // All good - Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanSeeAccountAccessForAnyUser.toString()) - val goodResponseGet = makeGetRequest(requestGet <@ user1) - goodResponseGet.code should equal(200) - goodResponseGet.body.extract[AccountsMinimalJson400] - } } From a40f218351945c16e39854ec46c98efaefbaee5c Mon Sep 17 00:00:00 2001 From: tawoe Date: Thu, 13 Jun 2024 12:43:05 +0200 Subject: [PATCH 31/75] build containers for pull requests --- .github/workflows/build_pull_request.yml | 102 +++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 .github/workflows/build_pull_request.yml diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml new file mode 100644 index 000000000..d7365eca7 --- /dev/null +++ b/.github/workflows/build_pull_request.yml @@ -0,0 +1,102 @@ +name: Build on Pull Request + +on: + pull_request: + branches: + - '**' +env: + ## Sets environment variable + DOCKER_HUB_ORGANIZATION: ${{ vars.DOCKER_HUB_ORGANIZATION }} + + +jobs: + build: + runs-on: ubuntu-latest + services: + # Label used to access the service container + redis: + # Docker Hub image + image: redis + ports: + # Opens tcp port 6379 on the host and service container + - 6379:6379 + # Set health checks to wait until redis has started + options: >- + --health-cmd "redis-cli ping" + --health-interval 10s + --health-timeout 5s + --health-retries 5 + steps: + - uses: actions/checkout@v3 + - name: Set up JDK 11 + uses: actions/setup-java@v2 + with: + java-version: '11' + distribution: 'adopt' + cache: maven + - name: Build with Maven + run: | + cp obp-api/src/main/resources/props/sample.props.template obp-api/src/main/resources/props/production.default.props + echo connector=star > obp-api/src/main/resources/props/test.default.props + echo starConnector_supported_types=mapped,internal >> obp-api/src/main/resources/props/test.default.props + echo hostname=http://localhost:8016 >> obp-api/src/main/resources/props/test.default.props + echo tests.port=8016 >> obp-api/src/main/resources/props/test.default.props + echo End of minimum settings >> obp-api/src/main/resources/props/test.default.props + echo payments_enabled=false >> obp-api/src/main/resources/props/test.default.props + echo importer_secret=change_me >> obp-api/src/main/resources/props/test.default.props + echo messageQueue.updateBankAccountsTransaction=false >> obp-api/src/main/resources/props/test.default.props + echo messageQueue.createBankAccounts=false >> obp-api/src/main/resources/props/test.default.props + echo allow_sandbox_account_creation=true >> obp-api/src/main/resources/props/test.default.props + echo allow_sandbox_data_import=true >> obp-api/src/main/resources/props/test.default.props + echo sandbox_data_import_secret=change_me >> obp-api/src/main/resources/props/test.default.props + echo allow_account_deletion=true >> obp-api/src/main/resources/props/test.default.props + echo allowed_internal_redirect_urls = /,/oauth/authorize >> obp-api/src/main/resources/props/test.default.props + echo transactionRequests_enabled=true >> obp-api/src/main/resources/props/test.default.props + echo transactionRequests_supported_types=SEPA,SANDBOX_TAN,FREE_FORM,COUNTERPARTY,ACCOUNT,SIMPLE >> obp-api/src/main/resources/props/test.default.props + echo SIMPLE_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props + echo openredirects.hostname.whitlelist=http://127.0.0.1,http://localhost >> obp-api/src/main/resources/props/test.default.props + echo remotedata.secret = foobarbaz >> obp-api/src/main/resources/props/test.default.props + echo allow_public_views=true >> obp-api/src/main/resources/props/test.default.props + + echo SIMPLE_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props + echo ACCOUNT_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props + echo SEPA_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props + echo FREE_FORM_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props + echo COUNTERPARTY_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props + echo SEPA_CREDIT_TRANSFERS_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props + + echo kafka.akka.timeout = 9 >> obp-api/src/main/resources/props/test.default.props + echo remotedata.timeout = 10 >> obp-api/src/main/resources/props/test.default.props + + echo allow_oauth2_login=true >> obp-api/src/main/resources/props/test.default.props + echo oauth2.jwk_set.url=https://www.googleapis.com/oauth2/v3/certs >> obp-api/src/main/resources/props/test.default.props + + echo ResetPasswordUrlEnabled=true >> obp-api/src/main/resources/props/test.default.props + + echo consents.allowed=true >> obp-api/src/main/resources/props/test.default.props + MAVEN_OPTS="-Xmx3G -Xss2m" mvn clean package -Pprod + - name: Build the Docker image + run: | + echo "${{ secrets.DOCKER_HUB_TOKEN }}" | docker login -u "${{ secrets.DOCKER_HUB_USERNAME }}" --password-stdin docker.io + docker build . --file .github/Dockerfile_PreBuild --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/obp-api-${{ github.event.pull_request.user.login }}:$GITHUB_SHA --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/obp-api-${{ github.event.pull_request.user.login }}:latest + docker push docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }} --all-tags + echo docker done + + - uses: sigstore/cosign-installer@main + + - name: Write signing key to disk (only needed for `cosign sign --key`) + run: echo "${{ secrets.COSIGN_PRIVATE_KEY }}" > cosign.key + + - name: Sign container image + run: | + cosign sign -y --key cosign.key \ + docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:develop + cosign sign -y --key cosign.key \ + docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/obp-api-${{ github.event.pull_request.user.login }}:latest + cosign sign -y --key cosign.key \ + docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/obp-api-${{ github.event.pull_request.user.login }}:$GITHUB_SHA + env: + COSIGN_PASSWORD: "${{secrets.COSIGN_PASSWORD}}" + + + From 7de0276e6b79b352772d4ac074e43859839bfebb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Thu, 13 Jun 2024 14:16:42 +0200 Subject: [PATCH 32/75] feature/Set consumer into call context in case of consent auth hadeer --- .../main/scala/code/api/util/APIUtil.scala | 7 ++-- .../scala/code/api/util/ConsentUtil.scala | 34 +++++++++++-------- .../scala/code/api/v5_0_0/APIMethods500.scala | 11 +++++- .../scala/code/api/v5_1_0/APIMethods510.scala | 4 ++- .../scala/code/consent/ConsentProvider.scala | 2 +- .../scala/code/consent/MappedConsent.scala | 3 +- 6 files changed, 41 insertions(+), 20 deletions(-) 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 73079dc6d..6899d7631 100644 --- a/obp-api/src/main/scala/code/api/util/APIUtil.scala +++ b/obp-api/src/main/scala/code/api/util/APIUtil.scala @@ -2971,8 +2971,11 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{ } else if (APIUtil.hasConsentJWT(reqHeaders)) { // Open Bank Project's Consent val consentValue = APIUtil.getConsentJWT(reqHeaders) Consent.getConsentJwtValueByConsentId(consentValue.getOrElse("")) match { - case Some(jwt) => // JWT value obtained via "Consent-Id" request header - Consent.applyRules(Some(jwt), cc) + case Some(consent) => // JWT value obtained via "Consent-Id" request header + Consent.applyRules( + Some(consent.jsonWebToken), + cc.copy(consumer = Consumers.consumers.vend.getConsumerByConsumerId(consent.consumerId)) + ) case _ => JwtUtil.checkIfStringIsJWTValue(consentValue.getOrElse("")).isDefined match { case true => // It's JWT obtained via "Consent-JWT" request header diff --git a/obp-api/src/main/scala/code/api/util/ConsentUtil.scala b/obp-api/src/main/scala/code/api/util/ConsentUtil.scala index 86949f8dd..a0582859a 100644 --- a/obp-api/src/main/scala/code/api/util/ConsentUtil.scala +++ b/obp-api/src/main/scala/code/api/util/ConsentUtil.scala @@ -351,13 +351,16 @@ object Consent { case Full(jsonAsString) => try { val consent = net.liftweb.json.parse(jsonAsString).extract[ConsentJWT] - checkConsent(consent, consentAsJwt, callContext) match { // Check is it Consent-JWT expired + // Set Consumer into Call Context + val consumer = Consumers.consumers.vend.getConsumerByConsumerId(consent.aud) + val updatedCallContext = callContext.copy(consumer = consumer) + checkConsent(consent, consentAsJwt, updatedCallContext) match { // Check is it Consent-JWT expired case (Full(true)) => // OK applyConsentRules(consent) case failure@Failure(_, _, _) => // Handled errors - Future(failure, Some(callContext)) + Future(failure, Some(updatedCallContext)) case _ => // Unexpected errors - Future(Failure(ErrorMessages.ConsentCheckExpiredIssue), Some(callContext)) + Future(Failure(ErrorMessages.ConsentCheckExpiredIssue), Some(updatedCallContext)) } } catch { // Possible exceptions case e: ParseException => Future(Failure("ParseException: " + e.getMessage), Some(callContext)) @@ -387,11 +390,11 @@ object Consent { } } - def getConsentJwtValueByConsentId(consentId: String): Option[String] = { + def getConsentJwtValueByConsentId(consentId: String): Option[MappedConsent] = { APIUtil.checkIfStringIsUUIDVersion1(consentId) match { case true => // String is a UUID Consents.consentProvider.vend.getConsentByConsentId(consentId) match { - case Full(consent) => Some(consent.jsonWebToken) + case Full(consent) => Some(consent) case _ => None // It's not valid UUID value } case false => None // It's not UUID at all @@ -464,6 +467,9 @@ object Consent { // 1st we need to find a Consent via the field MappedConsent.consentId Consents.consentProvider.vend.getConsentByConsentId(consentId) match { case Full(storedConsent) => + // Set Consumer into Call Context + val consumer = Consumers.consumers.vend.getConsumerByConsumerId(storedConsent.consumerId) + val updatedCallContext = callContext.copy(consumer = consumer) // This function MUST be called only once per call. I.e. it's date dependent val (canBeUsed, currentCounterState) = checkFrequencyPerDay(storedConsent) if(canBeUsed) { @@ -471,28 +477,28 @@ object Consent { case Full(jsonAsString) => try { val consent = net.liftweb.json.parse(jsonAsString).extract[ConsentJWT] - checkConsent(consent, storedConsent.jsonWebToken, callContext) match { // Check is it Consent-JWT expired + checkConsent(consent, storedConsent.jsonWebToken, updatedCallContext) match { // Check is it Consent-JWT expired case (Full(true)) => // OK // Update MappedConsent.usesSoFarTodayCounter field Consents.consentProvider.vend.updateBerlinGroupConsent(consentId, currentCounterState + 1) applyConsentRules(consent) case failure@Failure(_, _, _) => // Handled errors - Future(failure, Some(callContext)) + Future(failure, Some(updatedCallContext)) case _ => // Unexpected errors - Future(Failure(ErrorMessages.ConsentCheckExpiredIssue), Some(callContext)) + Future(Failure(ErrorMessages.ConsentCheckExpiredIssue), Some(updatedCallContext)) } } catch { // Possible exceptions - case e: ParseException => Future(Failure("ParseException: " + e.getMessage), Some(callContext)) - case e: MappingException => Future(Failure("MappingException: " + e.getMessage), Some(callContext)) - case e: Exception => Future(Failure("parsing failed: " + e.getMessage), Some(callContext)) + case e: ParseException => Future(Failure("ParseException: " + e.getMessage), Some(updatedCallContext)) + case e: MappingException => Future(Failure("MappingException: " + e.getMessage), Some(updatedCallContext)) + case e: Exception => Future(Failure("parsing failed: " + e.getMessage), Some(updatedCallContext)) } case failure@Failure(_, _, _) => - Future(failure, Some(callContext)) + Future(failure, Some(updatedCallContext)) case _ => - Future(Failure("Cannot extract data from: " + consentId), Some(callContext)) + Future(Failure("Cannot extract data from: " + consentId), Some(updatedCallContext)) } } else { - Future(Failure(ErrorMessages.TooManyRequests + s" ${RequestHeader.`Consent-ID`}: $consentId"), Some(callContext)) + Future(Failure(ErrorMessages.TooManyRequests + s" ${RequestHeader.`Consent-ID`}: $consentId"), Some(updatedCallContext)) } case failure@Failure(_, _, _) => Future(failure, Some(callContext)) diff --git a/obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala b/obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala index 221925173..88fefb88a 100644 --- a/obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala +++ b/obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala @@ -45,6 +45,7 @@ import java.util.concurrent.ThreadLocalRandom import code.accountattribute.AccountAttributeX import code.api.Constant.SYSTEM_OWNER_VIEW_ID import code.api.util.FutureUtil.EndpointContext +import code.consumer.Consumers import code.util.Helper.booleanToFuture import code.views.system.{AccountAccess, ViewDefinition} @@ -991,7 +992,15 @@ trait APIMethods500 { case Props.RunModes.Test => Consent.challengeAnswerAtTestEnvironment case _ => SecureRandomUtil.numeric() } - createdConsent <- Future(Consents.consentProvider.vend.createObpConsent(user, challengeAnswer, Some(consentRequestId))) map { + consumer = Consumers.consumers.vend.getConsumerByConsumerId(calculatedConsumerId.getOrElse("None")) + createdConsent <- Future( + Consents.consentProvider.vend.createObpConsent( + user, + challengeAnswer, + Some(consentRequestId), + consumer + ) + ) map { i => connectorEmptyResponse(i, callContext) } diff --git a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala index b04786342..778e8d659 100644 --- a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala +++ b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala @@ -997,7 +997,9 @@ trait APIMethods510 { unboxFullOrFail(_, cc.callContext, ConsentNotFound) } _ <- Helper.booleanToFuture(failMsg = ConsentNotFound, cc = cc.callContext) { - consent.mUserId == cc.userId + consent.mConsumerId.get == null || + consent.mConsumerId.get == "" || + consent.mConsumerId.get == cc.consumer.map(_.consumerId.get).getOrElse("None") } } yield { (JSONFactory510.getConsentInfoJson(consent), HttpCode.`200`(cc)) diff --git a/obp-api/src/main/scala/code/consent/ConsentProvider.scala b/obp-api/src/main/scala/code/consent/ConsentProvider.scala index 1641adfa4..2a3fdf52e 100644 --- a/obp-api/src/main/scala/code/consent/ConsentProvider.scala +++ b/obp-api/src/main/scala/code/consent/ConsentProvider.scala @@ -21,7 +21,7 @@ trait ConsentProvider { def updateConsentStatus(consentId: String, status: ConsentStatus): Box[MappedConsent] def updateConsentUser(consentId: String, user: User): Box[MappedConsent] def getConsentsByUser(userId: String): List[MappedConsent] - def createObpConsent(user: User, challengeAnswer: String, consentRequestId:Option[String]): Box[MappedConsent] + def createObpConsent(user: User, challengeAnswer: String, consentRequestId:Option[String], consumer: Option[Consumer] = None): Box[MappedConsent] def setJsonWebToken(consentId: String, jwt: String): Box[MappedConsent] def revoke(consentId: String): Box[MappedConsent] def checkAnswer(consentId: String, challenge: String): Box[MappedConsent] diff --git a/obp-api/src/main/scala/code/consent/MappedConsent.scala b/obp-api/src/main/scala/code/consent/MappedConsent.scala index 6fe27b370..37734773d 100644 --- a/obp-api/src/main/scala/code/consent/MappedConsent.scala +++ b/obp-api/src/main/scala/code/consent/MappedConsent.scala @@ -62,13 +62,14 @@ object MappedConsentProvider extends ConsentProvider { override def getConsentsByUser(userId: String): List[MappedConsent] = { MappedConsent.findAll(By(MappedConsent.mUserId, userId)) } - override def createObpConsent(user: User, challengeAnswer: String, consentRequestId:Option[String]): Box[MappedConsent] = { + override def createObpConsent(user: User, challengeAnswer: String, consentRequestId:Option[String], consumer: Option[Consumer]): Box[MappedConsent] = { tryo { val salt = BCrypt.gensalt() val challengeAnswerHashed = BCrypt.hashpw(challengeAnswer, salt).substring(0, 44) MappedConsent .create .mUserId(user.userId) + .mConsumerId(consumer.map(_.consumerId.get).getOrElse(null)) .mConsentRequestId(consentRequestId.getOrElse(null)) .mChallenge(challengeAnswerHashed) .mSalt(salt) From 5488218bc80d74ffe11155ecc6153229fe957df9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Thu, 13 Jun 2024 14:52:35 +0200 Subject: [PATCH 33/75] refactor/Tweak functions names at ConsentUtil.scala --- .../scala/code/api/util/ConsentUtil.scala | 22 +++++-------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/obp-api/src/main/scala/code/api/util/ConsentUtil.scala b/obp-api/src/main/scala/code/api/util/ConsentUtil.scala index a0582859a..bd709244d 100644 --- a/obp-api/src/main/scala/code/api/util/ConsentUtil.scala +++ b/obp-api/src/main/scala/code/api/util/ConsentUtil.scala @@ -272,7 +272,7 @@ object Consent { if (result.forall(_ == "Added")) Full(user) else Failure("Cannot add permissions to the user with id: " + user.userId) } - private def hasConsentInternalOldStyle(consentIdAsJwt: String, calContext: CallContext): Box[User] = { + private def applyConsentRulesCommonOldStyle(consentIdAsJwt: String, calContext: CallContext): Box[User] = { implicit val dateFormats = CustomJsonFormats.formats def applyConsentRules(consent: ConsentJWT): Box[User] = { @@ -316,7 +316,7 @@ object Consent { } } - private def hasConsentCommon(consentAsJwt: String, callContext: CallContext): Future[(Box[User], Option[CallContext])] = { + private def applyConsentRulesCommon(consentAsJwt: String, callContext: CallContext): Future[(Box[User], Option[CallContext])] = { implicit val dateFormats = CustomJsonFormats.formats def applyConsentRules(consent: ConsentJWT): Future[(Box[User], Option[CallContext])] = { @@ -374,17 +374,10 @@ object Consent { } } - private def hasConsentOldStyle(consentIdAsJwt: String, callContext: CallContext): (Box[User], CallContext) = { - (hasConsentInternalOldStyle(consentIdAsJwt, callContext), callContext) - } - private def hasConsent(consentAsJwt: String, callContext: CallContext): Future[(Box[User], Option[CallContext])] = { - hasConsentCommon(consentAsJwt, callContext) - } - def applyRules(consentJwt: Option[String], callContext: CallContext): Future[(Box[User], Option[CallContext])] = { val allowed = APIUtil.getPropsAsBoolValue(nameOfProperty="consents.allowed", defaultValue=false) (consentJwt, allowed) match { - case (Some(consentId), true) => hasConsent(consentId, callContext) + case (Some(jwt), true) => applyConsentRulesCommon(jwt, callContext) case (_, false) => Future((Failure(ErrorMessages.ConsentDisabled), Some(callContext))) case (None, _) => Future((Failure(ErrorMessages.ConsentHeaderNotFound), Some(callContext))) } @@ -410,7 +403,7 @@ object Consent { Full(Nil) } } - private def hasBerlinGroupConsentInternal(consentId: String, callContext: CallContext): Future[(Box[User], Option[CallContext])] = { + private def applyBerlinGroupConsentRulesCommon(consentId: String, callContext: CallContext): Future[(Box[User], Option[CallContext])] = { implicit val dateFormats = CustomJsonFormats.formats def applyConsentRules(consent: ConsentJWT): Future[(Box[User], Option[CallContext])] = { @@ -506,13 +499,10 @@ object Consent { Future(Failure(ErrorMessages.ConsentNotFound + s" ($consentId)"), Some(callContext)) } } - private def hasBerlinGroupConsent(consentId: String, callContext: CallContext): Future[(Box[User], Option[CallContext])] = { - hasBerlinGroupConsentInternal(consentId, callContext) - } def applyBerlinGroupRules(consentId: Option[String], callContext: CallContext): Future[(Box[User], Option[CallContext])] = { val allowed = APIUtil.getPropsAsBoolValue(nameOfProperty="consents.allowed", defaultValue=false) (consentId, allowed) match { - case (Some(consentId), true) => hasBerlinGroupConsent(consentId, callContext) + case (Some(consentId), true) => applyBerlinGroupConsentRulesCommon(consentId, callContext) case (_, false) => Future((Failure(ErrorMessages.ConsentDisabled), Some(callContext))) case (None, _) => Future((Failure(ErrorMessages.ConsentHeaderNotFound), Some(callContext))) } @@ -520,7 +510,7 @@ object Consent { def applyRulesOldStyle(consentId: Option[String], callContext: CallContext): (Box[User], CallContext) = { val allowed = APIUtil.getPropsAsBoolValue(nameOfProperty="consents.allowed", defaultValue=false) (consentId, allowed) match { - case (Some(consentId), true) => hasConsentOldStyle(consentId, callContext) + case (Some(consentId), true) => (applyConsentRulesCommonOldStyle(consentId, callContext), callContext) case (_, false) => (Failure(ErrorMessages.ConsentDisabled), callContext) case (None, _) => (Failure(ErrorMessages.ConsentHeaderNotFound), callContext) } From 3b4ccbe478cdf61b4aa2820535f1699d655d637e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Fri, 14 Jun 2024 11:52:24 +0200 Subject: [PATCH 34/75] refactor/Tweak property consumer_validation_method_for_consent --- .../src/main/resources/props/sample.props.template | 6 +++++- .../src/main/scala/code/api/util/ConsentUtil.scala | 11 ++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/obp-api/src/main/resources/props/sample.props.template b/obp-api/src/main/resources/props/sample.props.template index 0498f5ad7..8a1860ef4 100644 --- a/obp-api/src/main/resources/props/sample.props.template +++ b/obp-api/src/main/resources/props/sample.props.template @@ -881,7 +881,11 @@ database_messages_scheduler_interval=3600 # -- Consents --------------------------------------------- # In case isn't defined default value is "false" # consents.allowed=true -# consumer_validation_method_for_consent=CONSUMER_KEY_VALUE +# +# In order to pin a consent to a consumer we can use the property consumer_validation_method_for_consent +# Possibile values are: CONSUMER_CERTIFICATE, CONSUMER_KEY_VALUE, NONE +# consumer_validation_method_for_consent=CONSUMER_CERTIFICATE +# # consents.max_time_to_live=3600 # In case isn't defined default value is "false" # consents.sca.enabled=true diff --git a/obp-api/src/main/scala/code/api/util/ConsentUtil.scala b/obp-api/src/main/scala/code/api/util/ConsentUtil.scala index bd709244d..e7b1d46f2 100644 --- a/obp-api/src/main/scala/code/api/util/ConsentUtil.scala +++ b/obp-api/src/main/scala/code/api/util/ConsentUtil.scala @@ -15,6 +15,7 @@ import code.context.{ConsentAuthContextProvider, UserAuthContextProvider} import code.entitlement.Entitlement import code.model.Consumer import code.users.Users +import code.util.Helper.MdcLoggable import code.util.HydraUtil import code.views.Views import com.nimbusds.jwt.JWTClaimsSet @@ -25,7 +26,7 @@ import net.liftweb.http.provider.HTTPParam import net.liftweb.json.JsonParser.ParseException import net.liftweb.json.{Extraction, MappingException, compactRender, parse} import net.liftweb.mapper.By -import net.liftweb.util.ControlHelpers +import net.liftweb.util.{ControlHelpers, Props} import sh.ory.hydra.model.OAuth2TokenIntrospection import scala.collection.immutable.{List, Nil} @@ -104,7 +105,7 @@ case class Consent(createdByUserId: String, } } -object Consent { +object Consent extends MdcLoggable { final lazy val challengeAnswerAtTestEnvironment = "123" @@ -126,7 +127,11 @@ object Consent { private def checkConsumerIsActiveAndMatched(consent: ConsentJWT, callContext: CallContext): Box[Boolean] = { Consumers.consumers.vend.getConsumerByConsumerId(consent.aud) match { case Full(consumerFromConsent) if consumerFromConsent.isActive.get == true => // Consumer is active - APIUtil.getPropsValue(nameOfProperty = "consumer_validation_method_for_consent", defaultValue = "CONSUMER_KEY_VALUE") match { + val validationMetod = APIUtil.getPropsValue(nameOfProperty = "consumer_validation_method_for_consent", defaultValue = "CONSUMER_CERTIFICATE") + if(validationMetod != "CONSUMER_CERTIFICATE" && Props.mode == Props.RunModes.Production) { + logger.warn(s"consumer_validation_method_for_consent is not set to CONSUMER_CERTIFICATE! The current value is: ${validationMetod}") + } + validationMetod match { case "CONSUMER_KEY_VALUE" => val requestHeaderConsumerKey = getConsumerKey(callContext.requestHeaders) requestHeaderConsumerKey match { From 9d27fc543bcc7cf89f995756cafeabb06cf79398 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Fri, 14 Jun 2024 13:04:52 +0200 Subject: [PATCH 35/75] docfix/Add TODO: Get the source of truth for Consumer (e.g. CONSUMER_CERTIFICATE) as early as possible --- obp-api/src/main/scala/code/api/util/APIUtil.scala | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 6899d7631..48c6f66a1 100644 --- a/obp-api/src/main/scala/code/api/util/APIUtil.scala +++ b/obp-api/src/main/scala/code/api/util/APIUtil.scala @@ -2973,7 +2973,10 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{ Consent.getConsentJwtValueByConsentId(consentValue.getOrElse("")) match { case Some(consent) => // JWT value obtained via "Consent-Id" request header Consent.applyRules( - Some(consent.jsonWebToken), + Some(consent.jsonWebToken), + // Note: At this point we are getting the Consumer from the Consumer in the Consent. + // This may later be cross checked via the value in consumer_validation_method_for_consent. + // TODO: Get the source of truth for Consumer (e.g. CONSUMER_CERTIFICATE) as early as possible. cc.copy(consumer = Consumers.consumers.vend.getConsumerByConsumerId(consent.consumerId)) ) case _ => From e39e4691341719de84fe3532a10327a8d37d3071 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Fri, 14 Jun 2024 13:15:19 +0200 Subject: [PATCH 36/75] feature/Tweak guard at endpoint getConsentByConsentId --- obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala index 778e8d659..b04786342 100644 --- a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala +++ b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala @@ -997,9 +997,7 @@ trait APIMethods510 { unboxFullOrFail(_, cc.callContext, ConsentNotFound) } _ <- Helper.booleanToFuture(failMsg = ConsentNotFound, cc = cc.callContext) { - consent.mConsumerId.get == null || - consent.mConsumerId.get == "" || - consent.mConsumerId.get == cc.consumer.map(_.consumerId.get).getOrElse("None") + consent.mUserId == cc.userId } } yield { (JSONFactory510.getConsentInfoJson(consent), HttpCode.`200`(cc)) From 3d068cf1dda019c7864984caa4021ab7e7f2b3c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Fri, 14 Jun 2024 14:48:07 +0200 Subject: [PATCH 37/75] test/Fix failing tests --- obp-api/src/test/scala/code/api/v3_1_0/ConsentTest.scala | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/obp-api/src/test/scala/code/api/v3_1_0/ConsentTest.scala b/obp-api/src/test/scala/code/api/v3_1_0/ConsentTest.scala index ca2de8bb7..6449695a5 100644 --- a/obp-api/src/test/scala/code/api/v3_1_0/ConsentTest.scala +++ b/obp-api/src/test/scala/code/api/v3_1_0/ConsentTest.scala @@ -102,19 +102,27 @@ class ConsentTest extends V310ServerSetup { } scenario("We will call the endpoint with user credentials", ApiEndpoint1, ApiEndpoint3, VersionOfApi, VersionOfApi2) { + setPropsValues("consumer_validation_method_for_consent"-> "CONSUMER_KEY_VALUE") wholeFunctionality(RequestHeader.`Consent-JWT`) + setPropsValues("consumer_validation_method_for_consent"-> "CONSUMER_CERTIFICATE") } scenario("We will call the endpoint with user credentials and deprecated header name", ApiEndpoint1, ApiEndpoint3, VersionOfApi, VersionOfApi2) { + setPropsValues("consumer_validation_method_for_consent"-> "CONSUMER_KEY_VALUE") wholeFunctionality(RequestHeader.`Consent-Id`) + setPropsValues("consumer_validation_method_for_consent"-> "CONSUMER_CERTIFICATE") } scenario("We will call the endpoint with user credentials-Implicit", ApiEndpoint1, ApiEndpoint3, VersionOfApi, VersionOfApi2) { + setPropsValues("consumer_validation_method_for_consent"-> "CONSUMER_KEY_VALUE") wholeFunctionalityImplicit(RequestHeader.`Consent-JWT`) + setPropsValues("consumer_validation_method_for_consent"-> "CONSUMER_CERTIFICATE") } scenario("We will call the endpoint with user credentials and deprecated header name-Implicit", ApiEndpoint1, ApiEndpoint3, VersionOfApi, VersionOfApi2) { + setPropsValues("consumer_validation_method_for_consent"-> "CONSUMER_KEY_VALUE") wholeFunctionalityImplicit(RequestHeader.`Consent-Id`) + setPropsValues("consumer_validation_method_for_consent"-> "CONSUMER_CERTIFICATE") } } From 1d9771cceca862e72759065e7294512eb94d5e5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Mon, 17 Jun 2024 09:37:36 +0200 Subject: [PATCH 38/75] feature/Add function getCurrentConsumerViaMtls --- .../src/main/scala/code/api/util/APIUtil.scala | 4 ++-- .../main/scala/code/api/util/ConsentUtil.scala | 18 ++++++++++++++++-- .../scala/code/consumer/ConsumerProvider.scala | 2 ++ obp-api/src/main/scala/code/model/OAuth.scala | 4 ++++ .../code/remotedata/RemotedataConsumers.scala | 3 +++ 5 files changed, 27 insertions(+), 4 deletions(-) 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 48c6f66a1..958fd43c5 100644 --- a/obp-api/src/main/scala/code/api/util/APIUtil.scala +++ b/obp-api/src/main/scala/code/api/util/APIUtil.scala @@ -2976,8 +2976,8 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{ Some(consent.jsonWebToken), // Note: At this point we are getting the Consumer from the Consumer in the Consent. // This may later be cross checked via the value in consumer_validation_method_for_consent. - // TODO: Get the source of truth for Consumer (e.g. CONSUMER_CERTIFICATE) as early as possible. - cc.copy(consumer = Consumers.consumers.vend.getConsumerByConsumerId(consent.consumerId)) + // Get the source of truth for Consumer (e.g. CONSUMER_CERTIFICATE) as early as possible. + cc.copy(consumer = Consent.getCurrentConsumerViaMtls(callContext = cc)) ) case _ => JwtUtil.checkIfStringIsJWTValue(consentValue.getOrElse("")).isDefined match { diff --git a/obp-api/src/main/scala/code/api/util/ConsentUtil.scala b/obp-api/src/main/scala/code/api/util/ConsentUtil.scala index e7b1d46f2..e78704fe3 100644 --- a/obp-api/src/main/scala/code/api/util/ConsentUtil.scala +++ b/obp-api/src/main/scala/code/api/util/ConsentUtil.scala @@ -119,6 +119,20 @@ object Consent extends MdcLoggable { case _ => None } } + /** + * Purpose of this helper function is to get the Consumer via MTLS info i.e. PEM certificate. + * @return the boxed Consumer + */ + def getCurrentConsumerViaMtls(callContext: CallContext): Box[Consumer] = { + val clientCert: String = APIUtil.`getPSD2-CERT`(callContext.requestHeaders) + .getOrElse(SecureRandomUtil.csprng.nextLong().toString) + def removeBreakLines(input: String) = input + .replace("\n", "") + .replace("\r", "") + Consumers.consumers.vend.getConsumerByPemCertificate(clientCert).or( + Consumers.consumers.vend.getConsumerByPemCertificate(removeBreakLines(clientCert)) + ) + } private def verifyHmacSignedJwt(jwtToken: String, c: MappedConsent): Boolean = { JwtUtil.verifyHmacSignedJwt(jwtToken, c.secret) @@ -357,7 +371,7 @@ object Consent extends MdcLoggable { try { val consent = net.liftweb.json.parse(jsonAsString).extract[ConsentJWT] // Set Consumer into Call Context - val consumer = Consumers.consumers.vend.getConsumerByConsumerId(consent.aud) + val consumer = getCurrentConsumerViaMtls(callContext) val updatedCallContext = callContext.copy(consumer = consumer) checkConsent(consent, consentAsJwt, updatedCallContext) match { // Check is it Consent-JWT expired case (Full(true)) => // OK @@ -466,7 +480,7 @@ object Consent extends MdcLoggable { Consents.consentProvider.vend.getConsentByConsentId(consentId) match { case Full(storedConsent) => // Set Consumer into Call Context - val consumer = Consumers.consumers.vend.getConsumerByConsumerId(storedConsent.consumerId) + val consumer = getCurrentConsumerViaMtls(callContext) val updatedCallContext = callContext.copy(consumer = consumer) // This function MUST be called only once per call. I.e. it's date dependent val (canBeUsed, currentCounterState) = checkFrequencyPerDay(storedConsent) diff --git a/obp-api/src/main/scala/code/consumer/ConsumerProvider.scala b/obp-api/src/main/scala/code/consumer/ConsumerProvider.scala index 25725e19f..4d4babfd6 100644 --- a/obp-api/src/main/scala/code/consumer/ConsumerProvider.scala +++ b/obp-api/src/main/scala/code/consumer/ConsumerProvider.scala @@ -28,6 +28,7 @@ trait ConsumersProvider { def getConsumerByPrimaryId(id: Long): Box[Consumer] def getConsumerByConsumerKey(consumerKey: String): Box[Consumer] def getConsumerByConsumerKeyFuture(consumerKey: String): Future[Box[Consumer]] + def getConsumerByPemCertificate(pem: String): Box[Consumer] def getConsumerByConsumerId(consumerId: String): Box[Consumer] def getConsumerByConsumerIdFuture(consumerId: String): Future[Box[Consumer]] def getConsumersByUserIdFuture(userId: String): Future[List[Consumer]] @@ -61,6 +62,7 @@ class RemotedataConsumersCaseClasses { case class getConsumerByPrimaryId(id: Long) case class getConsumerByConsumerKey(consumerKey: String) case class getConsumerByConsumerKeyFuture(consumerKey: String) + case class getConsumerByPemCertificate(pem: String) case class getConsumerByConsumerId(consumerId: String) case class getConsumerByConsumerIdFuture(consumerId: String) case class getConsumersByUserIdFuture(userId: String) diff --git a/obp-api/src/main/scala/code/model/OAuth.scala b/obp-api/src/main/scala/code/model/OAuth.scala index 14da03e21..abc410f9c 100644 --- a/obp-api/src/main/scala/code/model/OAuth.scala +++ b/obp-api/src/main/scala/code/model/OAuth.scala @@ -103,6 +103,10 @@ object MappedConsumersProvider extends ConsumersProvider with MdcLoggable { } } + def getConsumerByPemCertificate(pem: String): Box[Consumer] = { + Consumer.find(By(Consumer.clientCertificate, pem)) + } + def getConsumerByConsumerId(consumerId: String): Box[Consumer] = { Consumer.find(By(Consumer.consumerId, consumerId)) } diff --git a/obp-api/src/main/scala/code/remotedata/RemotedataConsumers.scala b/obp-api/src/main/scala/code/remotedata/RemotedataConsumers.scala index 714644782..ccb41bdee 100644 --- a/obp-api/src/main/scala/code/remotedata/RemotedataConsumers.scala +++ b/obp-api/src/main/scala/code/remotedata/RemotedataConsumers.scala @@ -20,6 +20,9 @@ object RemotedataConsumers extends ObpActorInit with ConsumersProvider { def getConsumerByPrimaryId(id: Long): Box[Consumer] = getValueFromFuture( (actor ? cc.getConsumerByPrimaryId(id)).mapTo[Box[Consumer]] ) + def getConsumerByPemCertificate(pem: String): Box[Consumer] = getValueFromFuture( + (actor ? cc.getConsumerByPemCertificate(pem)).mapTo[Box[Consumer]] + ) def getConsumerByConsumerId(consumerId: String): Box[Consumer] = getValueFromFuture( (actor ? cc.getConsumerByConsumerId(consumerId)).mapTo[Box[Consumer]] ) From 2a0f0862228c7f43d8fac8c8dbefa242ee5e40ee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Jun 2024 09:59:53 +0000 Subject: [PATCH 39/75] Bump org.postgresql:postgresql from 42.4.3 to 42.4.4 in /obp-api Bumps [org.postgresql:postgresql](https://github.com/pgjdbc/pgjdbc) from 42.4.3 to 42.4.4. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.4.3...REL42.4.4) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- obp-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/obp-api/pom.xml b/obp-api/pom.xml index c8cf9ca2f..6e0acaa94 100644 --- a/obp-api/pom.xml +++ b/obp-api/pom.xml @@ -114,7 +114,7 @@ org.postgresql postgresql - 42.4.3 + 42.4.4 From 6caecebb843073b661dbfd7403a875dd7a8b007e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Jun 2024 09:59:54 +0000 Subject: [PATCH 40/75] Bump com.nimbusds:nimbus-jose-jwt from 9.31 to 9.37.2 in /obp-api Bumps [com.nimbusds:nimbus-jose-jwt](https://bitbucket.org/connect2id/nimbus-jose-jwt) from 9.31 to 9.37.2. - [Changelog](https://bitbucket.org/connect2id/nimbus-jose-jwt/src/master/CHANGELOG.txt) - [Commits](https://bitbucket.org/connect2id/nimbus-jose-jwt/branches/compare/9.37.2..9.31) --- updated-dependencies: - dependency-name: com.nimbusds:nimbus-jose-jwt dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- obp-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/obp-api/pom.xml b/obp-api/pom.xml index c8cf9ca2f..4e2c7286e 100644 --- a/obp-api/pom.xml +++ b/obp-api/pom.xml @@ -299,7 +299,7 @@ com.nimbusds nimbus-jose-jwt - 9.31 + 9.37.2 com.github.OpenBankProject From a15b6997850d83858429af7dcdfdffd657c6d32e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Jun 2024 10:00:09 +0000 Subject: [PATCH 41/75] Bump org.elasticsearch:elasticsearch from 8.10.3 to 8.14.0 in /obp-api Bumps [org.elasticsearch:elasticsearch](https://github.com/elastic/elasticsearch) from 8.10.3 to 8.14.0. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Changelog](https://github.com/elastic/elasticsearch/blob/main/CHANGELOG.md) - [Commits](https://github.com/elastic/elasticsearch/compare/v8.10.3...v8.14.0) --- updated-dependencies: - dependency-name: org.elasticsearch:elasticsearch dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- obp-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/obp-api/pom.xml b/obp-api/pom.xml index 48277be03..07e6b5072 100644 --- a/obp-api/pom.xml +++ b/obp-api/pom.xml @@ -212,7 +212,7 @@ org.elasticsearch elasticsearch - 8.10.3 + 8.14.0 From a76e82ba3ba7f4837410abc1dba37aadfd8ca174 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Jun 2024 10:01:35 +0000 Subject: [PATCH 42/75] Bump org.apache.commons:commons-compress in /obp-api Bumps org.apache.commons:commons-compress from 1.23.0 to 1.26.0. --- updated-dependencies: - dependency-name: org.apache.commons:commons-compress dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- obp-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/obp-api/pom.xml b/obp-api/pom.xml index 2b35b7c4a..c96ecfc84 100644 --- a/obp-api/pom.xml +++ b/obp-api/pom.xml @@ -266,7 +266,7 @@ org.apache.commons commons-compress - 1.24.0 + 1.26.0 com.twitter From 3c32d257b9311ef6eca1effa3c989d9a56c17437 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Tue, 18 Jun 2024 17:01:01 +0200 Subject: [PATCH 43/75] feature/Tweak endpoint selfRevokeConsent v5.1.0 --- obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala index b04786342..51d093825 100644 --- a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala +++ b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala @@ -1095,7 +1095,7 @@ trait APIMethods510 { (Full(user), callContext) <- authenticatedAccess(cc) consentId = getConsentIdRequestHeaderValue(cc.requestHeaders).getOrElse("") _ <- Future(Consents.consentProvider.vend.getConsentByConsentId(consentId)) map { - unboxFullOrFail(_, callContext, ConsentNotFound) + unboxFullOrFail(_, callContext, ConsentNotFound, 404) } consent <- Future(Consents.consentProvider.vend.revoke(consentId)) map { i => connectorEmptyResponse(i, callContext) From 496c9aa7c183abf9697810310981b6869c79763e Mon Sep 17 00:00:00 2001 From: tesobe-daniel Date: Tue, 18 Jun 2024 23:21:30 +0200 Subject: [PATCH 44/75] Update resource docs --- .../main/scala/code/api/v2_1_0/APIMethods210.scala | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/obp-api/src/main/scala/code/api/v2_1_0/APIMethods210.scala b/obp-api/src/main/scala/code/api/v2_1_0/APIMethods210.scala index 0de30b8d5..6b6de47b0 100644 --- a/obp-api/src/main/scala/code/api/v2_1_0/APIMethods210.scala +++ b/obp-api/src/main/scala/code/api/v2_1_0/APIMethods210.scala @@ -607,15 +607,15 @@ trait APIMethods210 { "Answer Transaction Request Challenge", """In Sandbox mode, any string that can be converted to a positive integer will be accepted as an answer. | - |This endpoint totally depends on createTransactionRequest, it need get the following data from createTransactionRequest response body. + |This endpoint expects the following data as provided in the createTransactionRequest response body: | - |1)`TRANSACTION_REQUEST_TYPE` : is the same as createTransactionRequest request URL . + |1)`TRANSACTION_REQUEST_TYPE` : as per the selected createTransactionRequest type, part of the request URL. | - |2)`TRANSACTION_REQUEST_ID` : is the `id` field in createTransactionRequest response body. + |2)`TRANSACTION_REQUEST_ID` : the value of the `id` field of the createTransactionRequest response body. | - |3) `id` : is `challenge.id` field in createTransactionRequest response body. + |3) `id` : the value of `challenge.id` in the createTransactionRequest response body. | - |4) `answer` : must be `123`. if it is in sandbox mode. If it kafka mode, the answer can be got by phone message or other security ways. + |4) `answer` : Defaults to `123`, if running in sandbox mode. In production mode, the value will be sent via the configured SCA method. | """.stripMargin, challengeAnswerJSON, @@ -1736,4 +1736,4 @@ trait APIMethods210 { } } } -} \ No newline at end of file +} From ea479ac530aa82335e35d6562bda2a773a11faa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Wed, 19 Jun 2024 08:37:47 +0200 Subject: [PATCH 45/75] feature/Tweak endpoint getConsentByConsentId v5.1.0 --- obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala index 51d093825..6dc6ca5d9 100644 --- a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala +++ b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala @@ -994,9 +994,9 @@ trait APIMethods510 { cc => implicit val ec = EndpointContext(Some(cc)) for { consent <- Future { Consents.consentProvider.vend.getConsentByConsentId(consentId)} map { - unboxFullOrFail(_, cc.callContext, ConsentNotFound) + unboxFullOrFail(_, cc.callContext, ConsentNotFound, 404) } - _ <- Helper.booleanToFuture(failMsg = ConsentNotFound, cc = cc.callContext) { + _ <- Helper.booleanToFuture(failMsg = ConsentNotFound, failCode = 404, cc = cc.callContext) { consent.mUserId == cc.userId } } yield { From e0ac6a3e54a0c8e71d4641942a7db0962c22a930 Mon Sep 17 00:00:00 2001 From: tawoe Date: Thu, 20 Jun 2024 09:35:12 +0200 Subject: [PATCH 46/75] create commiter containers --- .../workflows/build_contributer_container.yml | 68 +++++++++++++++++++ .github/workflows/build_pull_request.yml | 29 +++----- 2 files changed, 76 insertions(+), 21 deletions(-) create mode 100644 .github/workflows/build_contributer_container.yml diff --git a/.github/workflows/build_contributer_container.yml b/.github/workflows/build_contributer_container.yml new file mode 100644 index 000000000..30ed44442 --- /dev/null +++ b/.github/workflows/build_contributer_container.yml @@ -0,0 +1,68 @@ +name: Comment on the pull request + +# read-write repo token +# access to secrets +on: + workflow_run: + workflows: [Build on Pull Request] + types: + - completed + +jobs: + upload: + runs-on: ubuntu-latest + if: > + github.event.workflow_run.event == 'pull_request' && + github.event.workflow_run.conclusion == 'success' + steps: + - name: 'Download artifact' + uses: actions/github-script@v3.1.0 + with: + script: | + var artifacts = await github.actions.listWorkflowRunArtifacts({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: ${{github.event.workflow_run.id }}, + }); + var matchArtifact = artifacts.data.artifacts.filter((artifact) => { + return artifact.name == "pr" + })[0]; + var download = await github.actions.downloadArtifact({ + owner: context.repo.owner, + repo: context.repo.repo, + artifact_id: matchArtifact.id, + archive_format: 'zip', + }); + var fs = require('fs'); + fs.writeFileSync('${{github.workspace}}/pr.zip', Buffer.from(download.data)); + - run: unzip pr.zip + - name: Get user from file + run: echo "USER_NAME=$(pr/UN)" >> $GITHUB_ENV + + - name: prepare the artifact + run: | + mkdir -p obp-api/target/ + cp pr/obp-api-1.*.war obp-api/target/obp-api-1.10.1.war + + - name: Build the Docker image + run: | + echo "${{ secrets.DOCKER_HUB_TOKEN }}" | docker login -u "${{ secrets.DOCKER_HUB_USERNAME }}" --password-stdin docker.io + docker build . --file .github/Dockerfile_PreBuild --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/obp-api-${{ env.USER_NAME }}:$GITHUB_SHA --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/obp-api-${{ env.USER_NAME }}:latest + docker push docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }} --all-tags + echo docker done + + - uses: sigstore/cosign-installer@main + + - name: Write signing key to disk (only needed for `cosign sign --key`) + run: echo "${{ secrets.COSIGN_PRIVATE_KEY }}" > cosign.key + + - name: Sign container image + run: | + cosign sign -y --key cosign.key \ + docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:develop + cosign sign -y --key cosign.key \ + docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/obp-api-${{ github.event.pull_request.user.login }}:latest + cosign sign -y --key cosign.key \ + docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/obp-api-${{ github.event.pull_request.user.login }}:$GITHUB_SHA + env: + COSIGN_PASSWORD: "${{secrets.COSIGN_PASSWORD}}" \ No newline at end of file diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index d7365eca7..894c7c226 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -75,28 +75,15 @@ jobs: echo consents.allowed=true >> obp-api/src/main/resources/props/test.default.props MAVEN_OPTS="-Xmx3G -Xss2m" mvn clean package -Pprod - - name: Build the Docker image + - name: Save user name and .war artifact run: | - echo "${{ secrets.DOCKER_HUB_TOKEN }}" | docker login -u "${{ secrets.DOCKER_HUB_USERNAME }}" --password-stdin docker.io - docker build . --file .github/Dockerfile_PreBuild --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/obp-api-${{ github.event.pull_request.user.login }}:$GITHUB_SHA --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/obp-api-${{ github.event.pull_request.user.login }}:latest - docker push docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }} --all-tags - echo docker done - - - uses: sigstore/cosign-installer@main - - - name: Write signing key to disk (only needed for `cosign sign --key`) - run: echo "${{ secrets.COSIGN_PRIVATE_KEY }}" > cosign.key - - - name: Sign container image - run: | - cosign sign -y --key cosign.key \ - docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:develop - cosign sign -y --key cosign.key \ - docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/obp-api-${{ github.event.pull_request.user.login }}:latest - cosign sign -y --key cosign.key \ - docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/obp-api-${{ github.event.pull_request.user.login }}:$GITHUB_SHA - env: - COSIGN_PASSWORD: "${{secrets.COSIGN_PASSWORD}}" + mkdir -p ./pr + echo ${{ github.event.pull_request.user.login }} > ./pr/UN + cp obp-api/target/obp-api-1.*.war ./pr/ + - uses: actions/upload-artifact@v2 + with: + name: pr + path: pr/ From 01f2d3c3bf5365beb85f3c6e92f3f359ac202dee Mon Sep 17 00:00:00 2001 From: tawoe Date: Thu, 20 Jun 2024 10:14:17 +0200 Subject: [PATCH 47/75] split on push container workflow --- .github/workflows/build_container.yml | 78 +++++++++++++++++++ .../workflows/build_contributer_container.yml | 2 +- .github/workflows/build_package.yml | 44 +++-------- 3 files changed, 91 insertions(+), 33 deletions(-) create mode 100644 .github/workflows/build_container.yml diff --git a/.github/workflows/build_container.yml b/.github/workflows/build_container.yml new file mode 100644 index 000000000..bebcf29e5 --- /dev/null +++ b/.github/workflows/build_container.yml @@ -0,0 +1,78 @@ +name: Build and publish container + +# read-write repo token +# access to secrets +on: + workflow_run: + workflows: [build maven artifact] + types: + - completed + +env: + ## Sets environment variable + DOCKER_HUB_ORGANIZATION: ${{ vars.DOCKER_HUB_ORGANIZATION }} + DOCKER_HUB_REPOSITORY: obp-api + + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: 'Download artifact' + uses: actions/github-script@v3.1.0 + with: + script: | + var artifacts = await github.actions.listWorkflowRunArtifacts({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: ${{github.event.workflow_run.id }}, + }); + var matchArtifact = artifacts.data.artifacts.filter((artifact) => { + return artifact.name == "push" + })[0]; + var download = await github.actions.downloadArtifact({ + owner: context.repo.owner, + repo: context.repo.repo, + artifact_id: matchArtifact.id, + archive_format: 'zip', + }); + var fs = require('fs'); + fs.writeFileSync('${{github.workspace}}/push.zip', Buffer.from(download.data)); + - run: unzip push.zip + + - name: prepare the artifact + run: | + mkdir -p obp-api/target/ + cp push/obp-api-1.*.war obp-api/target/obp-api-1.10.1.war + + - name: Build the Docker image + run: | + echo "${{ secrets.DOCKER_HUB_TOKEN }}" | docker login -u "${{ secrets.DOCKER_HUB_USERNAME }}" --password-stdin docker.io + docker build . --file .github/Dockerfile_PreBuild --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:$GITHUB_SHA --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:latest --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:develop + docker build . --file .github/Dockerfile_PreBuild_OC --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:$GITHUB_SHA-OC --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:latest-OC --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:develop-OC --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:${GITHUB_REF##*/}-OC + docker push docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }} --all-tags + echo docker done + + - uses: sigstore/cosign-installer@main + + - name: Write signing key to disk (only needed for `cosign sign --key`) + run: echo "${{ secrets.COSIGN_PRIVATE_KEY }}" > cosign.key + + - name: Sign container image + run: | + cosign sign -y --key cosign.key \ + docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:develop + cosign sign -y --key cosign.key \ + docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:latest + cosign sign -y --key cosign.key \ + docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:$GITHUB_SHA + cosign sign -y --key cosign.key \ + docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:develop-OC + cosign sign -y --key cosign.key \ + docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:latest-OC + env: + COSIGN_PASSWORD: "${{secrets.COSIGN_PASSWORD}}" + + + diff --git a/.github/workflows/build_contributer_container.yml b/.github/workflows/build_contributer_container.yml index 30ed44442..899c3f4e2 100644 --- a/.github/workflows/build_contributer_container.yml +++ b/.github/workflows/build_contributer_container.yml @@ -1,4 +1,4 @@ -name: Comment on the pull request +name: Build and publish commiter container # read-write repo token # access to secrets diff --git a/.github/workflows/build_package.yml b/.github/workflows/build_package.yml index 9fac34f8a..56b0c50aa 100644 --- a/.github/workflows/build_package.yml +++ b/.github/workflows/build_package.yml @@ -1,11 +1,6 @@ -name: build and publish container +name: build maven artifact on: [push] -env: - ## Sets environment variable - DOCKER_HUB_ORGANIZATION: ${{ vars.DOCKER_HUB_ORGANIZATION }} - DOCKER_HUB_REPOSITORY: obp-api - jobs: build: @@ -73,33 +68,18 @@ jobs: echo consents.allowed=true >> obp-api/src/main/resources/props/test.default.props MAVEN_OPTS="-Xmx3G -Xss2m" mvn clean package -Pprod - - name: Build the Docker image + + - name: Save .war artifact run: | - echo "${{ secrets.DOCKER_HUB_TOKEN }}" | docker login -u "${{ secrets.DOCKER_HUB_USERNAME }}" --password-stdin docker.io - docker build . --file .github/Dockerfile_PreBuild --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:$GITHUB_SHA --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:latest --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:develop - docker build . --file .github/Dockerfile_PreBuild_OC --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:$GITHUB_SHA-OC --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:latest-OC --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:develop-OC --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:${GITHUB_REF##*/}-OC - docker push docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }} --all-tags - echo docker done - - - uses: sigstore/cosign-installer@main - - - name: Write signing key to disk (only needed for `cosign sign --key`) - run: echo "${{ secrets.COSIGN_PRIVATE_KEY }}" > cosign.key - - - name: Sign container image - run: | - cosign sign -y --key cosign.key \ - docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:develop - cosign sign -y --key cosign.key \ - docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:latest - cosign sign -y --key cosign.key \ - docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:$GITHUB_SHA - cosign sign -y --key cosign.key \ - docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:develop-OC - cosign sign -y --key cosign.key \ - docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:latest-OC - env: - COSIGN_PASSWORD: "${{secrets.COSIGN_PASSWORD}}" + mkdir -p ./push + cp obp-api/target/obp-api-1.*.war ./push/ + - uses: actions/upload-artifact@v2 + with: + name: push + path: push/ + + + From 3a3b801b5cae2a9da6000d301bd3b26c19adb5b6 Mon Sep 17 00:00:00 2001 From: tawoe Date: Thu, 20 Jun 2024 10:55:49 +0200 Subject: [PATCH 48/75] add :latest tag only for develop --- ...yml => build_container_develop_branch.yml} | 5 +- .../build_container_non_develop_branch.yml | 82 +++++++++++++++++++ .github/workflows/run_trivy.yml | 4 +- 3 files changed, 89 insertions(+), 2 deletions(-) rename .github/workflows/{build_container.yml => build_container_develop_branch.yml} (96%) create mode 100644 .github/workflows/build_container_non_develop_branch.yml diff --git a/.github/workflows/build_container.yml b/.github/workflows/build_container_develop_branch.yml similarity index 96% rename from .github/workflows/build_container.yml rename to .github/workflows/build_container_develop_branch.yml index bebcf29e5..07d0293cd 100644 --- a/.github/workflows/build_container.yml +++ b/.github/workflows/build_container_develop_branch.yml @@ -1,10 +1,12 @@ -name: Build and publish container +name: Build and publish container develop # read-write repo token # access to secrets on: workflow_run: workflows: [build maven artifact] + branches: + - develop types: - completed @@ -17,6 +19,7 @@ env: jobs: build: runs-on: ubuntu-latest + if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: - uses: actions/checkout@v3 - name: 'Download artifact' diff --git a/.github/workflows/build_container_non_develop_branch.yml b/.github/workflows/build_container_non_develop_branch.yml new file mode 100644 index 000000000..ed96c71c8 --- /dev/null +++ b/.github/workflows/build_container_non_develop_branch.yml @@ -0,0 +1,82 @@ +name: Build and publish container non develop + +# read-write repo token +# access to secrets +on: + workflow_run: + workflows: [build maven artifact] + branches: + - '*' + - '!develop' + types: + - completed + +env: + ## Sets environment variable + DOCKER_HUB_ORGANIZATION: ${{ vars.DOCKER_HUB_ORGANIZATION }} + DOCKER_HUB_REPOSITORY: obp-api + + +jobs: + build: + runs-on: ubuntu-latest + if: ${{ github.event.workflow_run.conclusion == 'success' }} + steps: + - uses: actions/checkout@v3 + - name: 'Download artifact' + uses: actions/github-script@v3.1.0 + with: + script: | + var artifacts = await github.actions.listWorkflowRunArtifacts({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: ${{github.event.workflow_run.id }}, + }); + var matchArtifact = artifacts.data.artifacts.filter((artifact) => { + return artifact.name == "push" + })[0]; + var download = await github.actions.downloadArtifact({ + owner: context.repo.owner, + repo: context.repo.repo, + artifact_id: matchArtifact.id, + archive_format: 'zip', + }); + var fs = require('fs'); + fs.writeFileSync('${{github.workspace}}/push.zip', Buffer.from(download.data)); + - run: unzip push.zip + + - name: prepare the artifact + run: | + mkdir -p obp-api/target/ + cp push/obp-api-1.*.war obp-api/target/obp-api-1.10.1.war + + - name: Build the Docker image + run: | + echo "${{ secrets.DOCKER_HUB_TOKEN }}" | docker login -u "${{ secrets.DOCKER_HUB_USERNAME }}" --password-stdin docker.io + docker build . --file .github/Dockerfile_PreBuild --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:$GITHUB_SHA --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:latest --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:develop + docker build . --file .github/Dockerfile_PreBuild_OC --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:$GITHUB_SHA-OC --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:latest-OC --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:develop-OC --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:${GITHUB_REF##*/}-OC + docker push docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }} --all-tags + echo docker done + + - uses: sigstore/cosign-installer@main + + - name: Write signing key to disk (only needed for `cosign sign --key`) + run: echo "${{ secrets.COSIGN_PRIVATE_KEY }}" > cosign.key + + - name: Sign container image + run: | + cosign sign -y --key cosign.key \ + docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:develop + cosign sign -y --key cosign.key \ + docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:latest + cosign sign -y --key cosign.key \ + docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:$GITHUB_SHA + cosign sign -y --key cosign.key \ + docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:develop-OC + cosign sign -y --key cosign.key \ + docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:latest-OC + env: + COSIGN_PASSWORD: "${{secrets.COSIGN_PASSWORD}}" + + + diff --git a/.github/workflows/run_trivy.yml b/.github/workflows/run_trivy.yml index a8a50366c..548cd92ad 100644 --- a/.github/workflows/run_trivy.yml +++ b/.github/workflows/run_trivy.yml @@ -2,7 +2,9 @@ name: scan container image on: workflow_run: - workflows: [build and publish container] + workflows: + - Build and publish container develop + - Build and publish container non develop types: - completed env: From 56b7cd24f18293239b6d4aa01ff12a0ed3d4d200 Mon Sep 17 00:00:00 2001 From: tawoe Date: Thu, 20 Jun 2024 12:29:32 +0200 Subject: [PATCH 49/75] bugfix build commiter container --- .github/workflows/build_contributer_container.yml | 2 +- .github/workflows/build_pull_request.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_contributer_container.yml b/.github/workflows/build_contributer_container.yml index 899c3f4e2..3665f1f41 100644 --- a/.github/workflows/build_contributer_container.yml +++ b/.github/workflows/build_contributer_container.yml @@ -42,7 +42,7 @@ jobs: - name: prepare the artifact run: | mkdir -p obp-api/target/ - cp pr/obp-api-1.*.war obp-api/target/obp-api-1.10.1.war + cp pr/obp-api-1.10.1.war obp-api/target/obp-api-1.10.1.war - name: Build the Docker image run: | diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index 894c7c226..eb3910655 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -79,7 +79,7 @@ jobs: run: | mkdir -p ./pr echo ${{ github.event.pull_request.user.login }} > ./pr/UN - cp obp-api/target/obp-api-1.*.war ./pr/ + cp obp-api/target/obp-api-1.10.1.war ./pr/obp-api-1.10.1.war - uses: actions/upload-artifact@v2 with: name: pr From 2347581dd5ed98376f4eb588cbdb6b3e21a91626 Mon Sep 17 00:00:00 2001 From: tawoe Date: Thu, 20 Jun 2024 12:32:35 +0200 Subject: [PATCH 50/75] bugfix debug commit: REVERT when done. --- .github/workflows/build_pull_request.yml | 43 +++--------------------- 1 file changed, 4 insertions(+), 39 deletions(-) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index eb3910655..22b1b8dc9 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -36,45 +36,10 @@ jobs: cache: maven - name: Build with Maven run: | - cp obp-api/src/main/resources/props/sample.props.template obp-api/src/main/resources/props/production.default.props - echo connector=star > obp-api/src/main/resources/props/test.default.props - echo starConnector_supported_types=mapped,internal >> obp-api/src/main/resources/props/test.default.props - echo hostname=http://localhost:8016 >> obp-api/src/main/resources/props/test.default.props - echo tests.port=8016 >> obp-api/src/main/resources/props/test.default.props - echo End of minimum settings >> obp-api/src/main/resources/props/test.default.props - echo payments_enabled=false >> obp-api/src/main/resources/props/test.default.props - echo importer_secret=change_me >> obp-api/src/main/resources/props/test.default.props - echo messageQueue.updateBankAccountsTransaction=false >> obp-api/src/main/resources/props/test.default.props - echo messageQueue.createBankAccounts=false >> obp-api/src/main/resources/props/test.default.props - echo allow_sandbox_account_creation=true >> obp-api/src/main/resources/props/test.default.props - echo allow_sandbox_data_import=true >> obp-api/src/main/resources/props/test.default.props - echo sandbox_data_import_secret=change_me >> obp-api/src/main/resources/props/test.default.props - echo allow_account_deletion=true >> obp-api/src/main/resources/props/test.default.props - echo allowed_internal_redirect_urls = /,/oauth/authorize >> obp-api/src/main/resources/props/test.default.props - echo transactionRequests_enabled=true >> obp-api/src/main/resources/props/test.default.props - echo transactionRequests_supported_types=SEPA,SANDBOX_TAN,FREE_FORM,COUNTERPARTY,ACCOUNT,SIMPLE >> obp-api/src/main/resources/props/test.default.props - echo SIMPLE_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props - echo openredirects.hostname.whitlelist=http://127.0.0.1,http://localhost >> obp-api/src/main/resources/props/test.default.props - echo remotedata.secret = foobarbaz >> obp-api/src/main/resources/props/test.default.props - echo allow_public_views=true >> obp-api/src/main/resources/props/test.default.props - - echo SIMPLE_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props - echo ACCOUNT_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props - echo SEPA_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props - echo FREE_FORM_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props - echo COUNTERPARTY_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props - echo SEPA_CREDIT_TRANSFERS_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props - - echo kafka.akka.timeout = 9 >> obp-api/src/main/resources/props/test.default.props - echo remotedata.timeout = 10 >> obp-api/src/main/resources/props/test.default.props - - echo allow_oauth2_login=true >> obp-api/src/main/resources/props/test.default.props - echo oauth2.jwk_set.url=https://www.googleapis.com/oauth2/v3/certs >> obp-api/src/main/resources/props/test.default.props - - echo ResetPasswordUrlEnabled=true >> obp-api/src/main/resources/props/test.default.props - - echo consents.allowed=true >> obp-api/src/main/resources/props/test.default.props - MAVEN_OPTS="-Xmx3G -Xss2m" mvn clean package -Pprod + pwd + ls obp-api + mkdir obp-api/target/ + touch obp-api/target/obp-api-1.10.1.war - name: Save user name and .war artifact run: | mkdir -p ./pr From bcc0be10c8d7dff6ca18275aec1d0e7df040da88 Mon Sep 17 00:00:00 2001 From: tawoe Date: Thu, 20 Jun 2024 12:52:43 +0200 Subject: [PATCH 51/75] bugfix debug commit: REVERT when done. --- .github/workflows/build_contributer_container.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build_contributer_container.yml b/.github/workflows/build_contributer_container.yml index 3665f1f41..ad20013c9 100644 --- a/.github/workflows/build_contributer_container.yml +++ b/.github/workflows/build_contributer_container.yml @@ -42,6 +42,7 @@ jobs: - name: prepare the artifact run: | mkdir -p obp-api/target/ + ls ./pr cp pr/obp-api-1.10.1.war obp-api/target/obp-api-1.10.1.war - name: Build the Docker image From e397b07e0b141e1303c1d7356023664d96bab693 Mon Sep 17 00:00:00 2001 From: tawoe Date: Thu, 20 Jun 2024 13:34:29 +0200 Subject: [PATCH 52/75] bugfix getUsername --- .github/workflows/build_contributer_container.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build_contributer_container.yml b/.github/workflows/build_contributer_container.yml index ad20013c9..df7b91022 100644 --- a/.github/workflows/build_contributer_container.yml +++ b/.github/workflows/build_contributer_container.yml @@ -36,8 +36,9 @@ jobs: var fs = require('fs'); fs.writeFileSync('${{github.workspace}}/pr.zip', Buffer.from(download.data)); - run: unzip pr.zip + - run: ls -al - name: Get user from file - run: echo "USER_NAME=$(pr/UN)" >> $GITHUB_ENV + run: echo "USER_NAME=$(cat pr/UN)" >> $GITHUB_ENV - name: prepare the artifact run: | From 1b947989a5cb12e764bd1e38cb9d5098fc9566cf Mon Sep 17 00:00:00 2001 From: tawoe Date: Thu, 20 Jun 2024 13:38:01 +0200 Subject: [PATCH 53/75] bugfix get artifact --- .github/workflows/build_contributer_container.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build_contributer_container.yml b/.github/workflows/build_contributer_container.yml index df7b91022..7440852c9 100644 --- a/.github/workflows/build_contributer_container.yml +++ b/.github/workflows/build_contributer_container.yml @@ -36,15 +36,13 @@ jobs: var fs = require('fs'); fs.writeFileSync('${{github.workspace}}/pr.zip', Buffer.from(download.data)); - run: unzip pr.zip - - run: ls -al - name: Get user from file - run: echo "USER_NAME=$(cat pr/UN)" >> $GITHUB_ENV + run: echo "USER_NAME=$(cat UN)" >> $GITHUB_ENV - name: prepare the artifact run: | mkdir -p obp-api/target/ - ls ./pr - cp pr/obp-api-1.10.1.war obp-api/target/obp-api-1.10.1.war + cp obp-api-1.10.1.war obp-api/target/obp-api-1.10.1.war - name: Build the Docker image run: | From bd7096cd209a0bd129d370f52f9de567bb865d08 Mon Sep 17 00:00:00 2001 From: tawoe Date: Thu, 20 Jun 2024 13:44:59 +0200 Subject: [PATCH 54/75] fix docker paths --- .github/workflows/build_contributer_container.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build_contributer_container.yml b/.github/workflows/build_contributer_container.yml index 7440852c9..9c61a7395 100644 --- a/.github/workflows/build_contributer_container.yml +++ b/.github/workflows/build_contributer_container.yml @@ -8,6 +8,10 @@ on: types: - completed +env: + ## Sets environment variable + DOCKER_HUB_ORGANIZATION: ${{ vars.DOCKER_HUB_ORGANIZATION }} + jobs: upload: runs-on: ubuntu-latest @@ -48,7 +52,7 @@ jobs: run: | echo "${{ secrets.DOCKER_HUB_TOKEN }}" | docker login -u "${{ secrets.DOCKER_HUB_USERNAME }}" --password-stdin docker.io docker build . --file .github/Dockerfile_PreBuild --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/obp-api-${{ env.USER_NAME }}:$GITHUB_SHA --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/obp-api-${{ env.USER_NAME }}:latest - docker push docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }} --all-tags + docker push docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/obp-api-${{ env.USER_NAME }} --all-tags echo docker done - uses: sigstore/cosign-installer@main From 3fa208e82b83284f530e5bd7b9f884f5ebf617d3 Mon Sep 17 00:00:00 2001 From: tawoe Date: Thu, 20 Jun 2024 13:45:13 +0200 Subject: [PATCH 55/75] fix artifact paths --- .github/workflows/build_container_develop_branch.yml | 2 +- .github/workflows/build_container_non_develop_branch.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_container_develop_branch.yml b/.github/workflows/build_container_develop_branch.yml index 07d0293cd..ddbeb6b3a 100644 --- a/.github/workflows/build_container_develop_branch.yml +++ b/.github/workflows/build_container_develop_branch.yml @@ -47,7 +47,7 @@ jobs: - name: prepare the artifact run: | mkdir -p obp-api/target/ - cp push/obp-api-1.*.war obp-api/target/obp-api-1.10.1.war + cp obp-api-1.10.1.war obp-api/target/obp-api-1.10.1.war - name: Build the Docker image run: | diff --git a/.github/workflows/build_container_non_develop_branch.yml b/.github/workflows/build_container_non_develop_branch.yml index ed96c71c8..b0837465d 100644 --- a/.github/workflows/build_container_non_develop_branch.yml +++ b/.github/workflows/build_container_non_develop_branch.yml @@ -48,7 +48,7 @@ jobs: - name: prepare the artifact run: | mkdir -p obp-api/target/ - cp push/obp-api-1.*.war obp-api/target/obp-api-1.10.1.war + cp obp-api-1.10.1.war obp-api/target/obp-api-1.10.1.war - name: Build the Docker image run: | From e18c69d6e4e3914b846a36d7feb4df3f9da12fe4 Mon Sep 17 00:00:00 2001 From: hongwei Date: Thu, 20 Jun 2024 12:39:11 +0200 Subject: [PATCH 56/75] feature/V5.1.0 added CounterpartyLimit Model --- .../counterpartylimit/CounterpartyLimit.scala | 45 +++++++ .../MappedCounterpartyLimit.scala | 118 ++++++++++++++++++ 2 files changed, 163 insertions(+) create mode 100644 obp-api/src/main/scala/code/counterpartylimit/CounterpartyLimit.scala create mode 100644 obp-api/src/main/scala/code/counterpartylimit/MappedCounterpartyLimit.scala diff --git a/obp-api/src/main/scala/code/counterpartylimit/CounterpartyLimit.scala b/obp-api/src/main/scala/code/counterpartylimit/CounterpartyLimit.scala new file mode 100644 index 000000000..c9302fa75 --- /dev/null +++ b/obp-api/src/main/scala/code/counterpartylimit/CounterpartyLimit.scala @@ -0,0 +1,45 @@ +package code.counterpartylimit + +import code.api.util.APIUtil +import net.liftweb.util.SimpleInjector +import net.liftweb.common.Box +import scala.concurrent.Future + +object CounterpartyLimitProvider extends SimpleInjector { + val rateLimiting = new Inject(buildOne _) {} + def buildOne: CounterpartyLimitProviderTrait = APIUtil.getPropsAsBoolValue("use_akka", false) match { + case _ => MappedCounterpartyLimitProvider +// case true => RemotedataCounterpartyLimit // we are getting rid of the akka now. so do not implement it here + } +} + +trait CounterpartyLimitProviderTrait { + def getAll(): Future[List[CounterpartyLimit]] + def getByCounterpartyLimitId(counterpartyLimitId: String): Future[Box[CounterpartyLimit]] + def deleteByCounterpartyLimitId(counterpartyLimitId: String): Future[Box[Boolean]] + def createOrUpdateCounterpartyLimit( + bankId: String, + accountId: String, + viewId: String, + counterpartyId: String, + maxSingleAmount: Int, + maxMonthlyAmount: Int, + maxNumberOfMonthlyTransactions: Int, + maxYearlyAmount: Int, + maxNumberOfYearlyTransactions: Int): Future[Box[CounterpartyLimit]] +} + +trait CounterpartyLimitTrait { + def counterpartyLimitId: String + def bankId: String + def accountId: String + def viewId: String + def counterpartyId: String + + def maxSingleAmount: Int + def maxMonthlyAmount: Int + def maxNumberOfMonthlyTransactions: Int + def maxYearlyAmount: Int + def maxNumberOfYearlyTransactions: Int +} + diff --git a/obp-api/src/main/scala/code/counterpartylimit/MappedCounterpartyLimit.scala b/obp-api/src/main/scala/code/counterpartylimit/MappedCounterpartyLimit.scala new file mode 100644 index 000000000..ba7502e11 --- /dev/null +++ b/obp-api/src/main/scala/code/counterpartylimit/MappedCounterpartyLimit.scala @@ -0,0 +1,118 @@ +package code.counterpartylimit + +import code.util.{MappedUUID} +import net.liftweb.common.{Box, Full} +import net.liftweb.mapper._ +import net.liftweb.util.Helpers.tryo + +import com.openbankproject.commons.ExecutionContext.Implicits.global +import scala.concurrent.Future + +object MappedCounterpartyLimitProvider extends CounterpartyLimitProviderTrait { + + def getAll(): Future[List[CounterpartyLimit]] = Future(CounterpartyLimit.findAll()) + def getByCounterpartyLimitId(counterpartyLimitId: String): Future[Box[CounterpartyLimit]] = Future { + CounterpartyLimit.find( + By(CounterpartyLimit.CounterpartyLimitId, counterpartyLimitId), + ) + } + def deleteByCounterpartyLimitId(counterpartyLimitId: String): Future[Box[Boolean]] = Future { + CounterpartyLimit.find( + By(CounterpartyLimit.CounterpartyLimitId, counterpartyLimitId), + ).map(_.delete_!) + } + + def createOrUpdateCounterpartyLimit( + bankId: String, + accountId: String, + viewId: String, + counterpartyId: String, + maxSingleAmount: Int, + maxMonthlyAmount: Int, + maxNumberOfMonthlyTransactions: Int, + maxYearlyAmount: Int, + maxNumberOfYearlyTransactions: Int): Future[Box[CounterpartyLimit]]= Future { + + def createCounterpartyLimit(counterpartyLimit: CounterpartyLimit): Box[CounterpartyLimit] = { + tryo { + counterpartyLimit.BankId(bankId) + counterpartyLimit.AccountId(accountId) + counterpartyLimit.ViewId(viewId) + counterpartyLimit.CounterpartyId(counterpartyId) + counterpartyLimit.MaxSingleAmount(maxSingleAmount) + counterpartyLimit.MaxMonthlyAmount(maxMonthlyAmount) + counterpartyLimit.MaxNumberOfMonthlyTransactions(maxNumberOfMonthlyTransactions) + counterpartyLimit.MaxYearlyAmount(maxYearlyAmount) + counterpartyLimit.MaxNumberOfYearlyTransactions(maxNumberOfYearlyTransactions) + counterpartyLimit.saveMe() + } + } + + val counterpartyLimit = CounterpartyLimit.find( + By(CounterpartyLimit.BankId, bankId), + By(CounterpartyLimit.AccountId, accountId), + By(CounterpartyLimit.ViewId, viewId), + By(CounterpartyLimit.CounterpartyId, counterpartyId), + ) + + val result = counterpartyLimit match { + case Full(limit) => createCounterpartyLimit(limit) + case _ => createCounterpartyLimit(CounterpartyLimit.create) + } + result + } +} + +class CounterpartyLimit extends CounterpartyLimitTrait with LongKeyedMapper[CounterpartyLimit] with IdPK with CreatedUpdated { + override def getSingleton = CounterpartyLimit + + object CounterpartyLimitId extends MappedUUID(this) + + object BankId extends MappedString(this, 255){ + override def dbNotNull_? = true + } + object AccountId extends MappedString(this, 255){ + override def dbNotNull_? = true + } + object ViewId extends MappedString(this, 255){ + override def dbNotNull_? = true + } + object CounterpartyId extends MappedString(this, 255){ + override def dbNotNull_? = true + } + + + object MaxSingleAmount extends MappedInt(this) { + override def defaultValue = -1 + } + object MaxMonthlyAmount extends MappedInt(this) { + override def defaultValue = -1 + } + object MaxNumberOfMonthlyTransactions extends MappedInt(this) { + override def defaultValue = -1 + } + object MaxYearlyAmount extends MappedInt(this) { + override def defaultValue = -1 + } + object MaxNumberOfYearlyTransactions extends MappedInt(this) { + override def defaultValue = -1 + } + + def counterpartyLimitId: String = CounterpartyLimitId.get + + def bankId: String = BankId.get + def accountId: String = AccountId.get + def viewId: String = ViewId.get + def counterpartyId: String = CounterpartyId.get + + def maxSingleAmount: Int = MaxSingleAmount.get + def maxMonthlyAmount: Int = MaxMonthlyAmount.get + def maxNumberOfMonthlyTransactions: Int = MaxNumberOfMonthlyTransactions.get + def maxYearlyAmount: Int = MaxYearlyAmount.get + def maxNumberOfYearlyTransactions: Int = MaxNumberOfYearlyTransactions.get + +} + +object CounterpartyLimit extends CounterpartyLimit with LongKeyedMetaMapper[CounterpartyLimit] { + override def dbIndexes = UniqueIndex(CounterpartyLimitId) :: UniqueIndex(BankId, AccountId, ViewId, CounterpartyId) :: super.dbIndexes +} From 906652ded9f1def376b25b81e5a3a5f5bff447e2 Mon Sep 17 00:00:00 2001 From: Simon Redfern Date: Thu, 20 Jun 2024 14:06:31 +0200 Subject: [PATCH 57/75] correcting view documentation private alias use --- obp-api/src/main/scala/code/api/v1_2_1/APIMethods121.scala | 2 +- obp-api/src/main/scala/code/api/v2_2_0/APIMethods220.scala | 2 +- obp-api/src/main/scala/code/api/v3_0_0/APIMethods300.scala | 2 +- obp-api/src/main/scala/code/api/v3_1_0/APIMethods310.scala | 2 +- obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/obp-api/src/main/scala/code/api/v1_2_1/APIMethods121.scala b/obp-api/src/main/scala/code/api/v1_2_1/APIMethods121.scala index 6ffec8325..ee5bb3031 100644 --- a/obp-api/src/main/scala/code/api/v1_2_1/APIMethods121.scala +++ b/obp-api/src/main/scala/code/api/v1_2_1/APIMethods121.scala @@ -568,7 +568,7 @@ trait APIMethods121 { | The 'alias' field in the JSON can take one of three values: | | * _public_: to use the public alias if there is one specified for the other account. - | * _private_: to use the public alias if there is one specified for the other account. + | * _private_: to use the private alias if there is one specified for the other account. | | * _''(empty string)_: to use no alias; the view shows the real name of the other account. | diff --git a/obp-api/src/main/scala/code/api/v2_2_0/APIMethods220.scala b/obp-api/src/main/scala/code/api/v2_2_0/APIMethods220.scala index 00531a24a..7d5c5a720 100644 --- a/obp-api/src/main/scala/code/api/v2_2_0/APIMethods220.scala +++ b/obp-api/src/main/scala/code/api/v2_2_0/APIMethods220.scala @@ -164,7 +164,7 @@ trait APIMethods220 { | The 'alias' field in the JSON can take one of three values: | | * _public_: to use the public alias if there is one specified for the other account. - | * _private_: to use the public alias if there is one specified for the other account. + | * _private_: to use the private alias if there is one specified for the other account. | | * _''(empty string)_: to use no alias; the view shows the real name of the other account. | diff --git a/obp-api/src/main/scala/code/api/v3_0_0/APIMethods300.scala b/obp-api/src/main/scala/code/api/v3_0_0/APIMethods300.scala index a939fd855..e3a103ca1 100644 --- a/obp-api/src/main/scala/code/api/v3_0_0/APIMethods300.scala +++ b/obp-api/src/main/scala/code/api/v3_0_0/APIMethods300.scala @@ -178,7 +178,7 @@ trait APIMethods300 { | The 'alias' field in the JSON can take one of three values: | | * _public_: to use the public alias if there is one specified for the other account. - | * _private_: to use the public alias if there is one specified for the other account. + | * _private_: to use the private alias if there is one specified for the other account. | | * _''(empty string)_: to use no alias; the view shows the real name of the other account. | diff --git a/obp-api/src/main/scala/code/api/v3_1_0/APIMethods310.scala b/obp-api/src/main/scala/code/api/v3_1_0/APIMethods310.scala index 74035de76..066746bb0 100644 --- a/obp-api/src/main/scala/code/api/v3_1_0/APIMethods310.scala +++ b/obp-api/src/main/scala/code/api/v3_1_0/APIMethods310.scala @@ -3952,7 +3952,7 @@ trait APIMethods310 { | The 'alias' field in the JSON can take one of two values: | | * _public_: to use the public alias if there is one specified for the other account. - | * _private_: to use the public alias if there is one specified for the other account. + | * _private_: to use the private alias if there is one specified for the other account. | | * _''(empty string)_: to use no alias; the view shows the real name of the other account. | diff --git a/obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala b/obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala index 88fefb88a..7852849d1 100644 --- a/obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala +++ b/obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala @@ -1864,7 +1864,7 @@ trait APIMethods500 { | The 'alias' field in the JSON can take one of two values: | | * _public_: to use the public alias if there is one specified for the other account. - | * _private_: to use the public alias if there is one specified for the other account. + | * _private_: to use the private alias if there is one specified for the other account. | | * _''(empty string)_: to use no alias; the view shows the real name of the other account. | From a7b5e70b0a3ed5a2ba4998cc6680c8a6320da2ef Mon Sep 17 00:00:00 2001 From: tawoe Date: Thu, 20 Jun 2024 14:19:24 +0200 Subject: [PATCH 58/75] checkout repo --- .github/workflows/build_contributer_container.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build_contributer_container.yml b/.github/workflows/build_contributer_container.yml index 9c61a7395..33ed5a8bf 100644 --- a/.github/workflows/build_contributer_container.yml +++ b/.github/workflows/build_contributer_container.yml @@ -19,6 +19,7 @@ jobs: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' steps: + - uses: actions/checkout@v3 - name: 'Download artifact' uses: actions/github-script@v3.1.0 with: From ea46c1ab51590e93b3a7983518135ff5a72c22ee Mon Sep 17 00:00:00 2001 From: tawoe Date: Thu, 20 Jun 2024 14:30:38 +0200 Subject: [PATCH 59/75] remove image signing --- .../workflows/build_contributer_container.yml | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/.github/workflows/build_contributer_container.yml b/.github/workflows/build_contributer_container.yml index 33ed5a8bf..d64fb7b18 100644 --- a/.github/workflows/build_contributer_container.yml +++ b/.github/workflows/build_contributer_container.yml @@ -55,19 +55,3 @@ jobs: docker build . --file .github/Dockerfile_PreBuild --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/obp-api-${{ env.USER_NAME }}:$GITHUB_SHA --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/obp-api-${{ env.USER_NAME }}:latest docker push docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/obp-api-${{ env.USER_NAME }} --all-tags echo docker done - - - uses: sigstore/cosign-installer@main - - - name: Write signing key to disk (only needed for `cosign sign --key`) - run: echo "${{ secrets.COSIGN_PRIVATE_KEY }}" > cosign.key - - - name: Sign container image - run: | - cosign sign -y --key cosign.key \ - docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:develop - cosign sign -y --key cosign.key \ - docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/obp-api-${{ github.event.pull_request.user.login }}:latest - cosign sign -y --key cosign.key \ - docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/obp-api-${{ github.event.pull_request.user.login }}:$GITHUB_SHA - env: - COSIGN_PASSWORD: "${{secrets.COSIGN_PASSWORD}}" \ No newline at end of file From cbebc6f9b85dfb679d4c702fe9379d8da9dc687f Mon Sep 17 00:00:00 2001 From: hongwei Date: Thu, 20 Jun 2024 22:34:51 +0200 Subject: [PATCH 60/75] feature/V5.1.0 added CounterpartyLimit endpoint -step1 --- .../SwaggerDefinitionsJSON.scala | 21 ++++++++++ .../main/scala/code/api/util/APIUtil.scala | 23 ++++++++++- .../src/main/scala/code/api/util/ApiTag.scala | 1 + .../main/scala/code/api/util/NewStyle.scala | 8 ++++ .../scala/code/api/v5_1_0/APIMethods510.scala | 40 ++++++++++++++++++- .../code/api/v5_1_0/JSONFactory5.1.0.scala | 21 ++++++++++ .../bankconnectors/LocalMappedConnector.scala | 6 +++ .../counterpartylimit/CounterpartyLimit.scala | 2 +- 8 files changed, 119 insertions(+), 3 deletions(-) diff --git a/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala b/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala index c4e3fd69b..fab74a589 100644 --- a/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala +++ b/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala @@ -5396,6 +5396,27 @@ object SwaggerDefinitionsJSON { phone = phoneExample.value, ) + val postCounterpartyLimitV510 = PostCounterpartyLimitV510( + max_single_amount = 0, + max_monthly_amount = 0, + max_number_of_monthly_transactions = 0, + max_yearly_amount = 0, + max_number_of_yearly_transactions = 0 + ) + + val counterpartyLimitV510 = CounterpartyLimitV510( + counterparty_limit_id = "", + bank_id = "", + account_id = "", + view_id = "", + counterparty_id = "", + max_single_amount = 0, + max_monthly_amount = 0, + max_number_of_monthly_transactions = 0, + max_yearly_amount = 0, + max_number_of_yearly_transactions = 0 + ) + val atmsJsonV510 = AtmsJsonV510( atms = List(atmJsonV510) ) 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 958fd43c5..8fd6ce2f1 100644 --- a/obp-api/src/main/scala/code/api/util/APIUtil.scala +++ b/obp-api/src/main/scala/code/api/util/APIUtil.scala @@ -1696,6 +1696,8 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{ private val isNeedCheckView = errorResponseBodies.contains($UserNoPermissionAccessView) && requestUrlPartPath.contains("BANK_ID") && requestUrlPartPath.contains("ACCOUNT_ID") && requestUrlPartPath.contains("VIEW_ID") + private val isNeedCheckCounterparty = errorResponseBodies.contains(CounterpartyNotFoundByCounterpartyId) && requestUrlPartPath.contains("COUNTERPARTY_ID") + private val reversedRequestUrl = requestUrlPartPath.reverse def getPathParams(url: List[String]): Map[String, String] = reversedRequestUrl.zip(url.reverse) collect { @@ -1775,6 +1777,14 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{ Future.successful(null.asInstanceOf[View]) } } + + def checkCounterparty(counterpartyId: Option[CounterpartyId], callContext: Option[CallContext]): OBPReturnType[CounterpartyTrait] = { + if(isNeedCheckCounterparty && counterpartyId.isDefined) { + checkCounterpartyFun(counterpartyId.get)(callContext) + } else { + Future.successful(null.asInstanceOf[CounterpartyTrait] -> callContext) + } + } // reset connectorMethods { val checkerFunctions = mutable.ListBuffer[PartialFunction[_, _]]() @@ -1795,6 +1805,9 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{ if (isNeedCheckView) { checkerFunctions += checkViewFun } + if (isNeedCheckCounterparty) { + checkerFunctions += checkCounterpartyFun + } val addedMethods: List[String] = checkerFunctions.toList.flatMap(getDependentConnectorMethods(_)) .map(value =>("obp." +value).intern()) @@ -1841,6 +1854,7 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{ val bankId = pathParams.get("BANK_ID").map(BankId(_)) val accountId = pathParams.get("ACCOUNT_ID").map(AccountId(_)) val viewId = pathParams.get("VIEW_ID").map(ViewId(_)) + val counterpartyId = pathParams.get("COUNTERPARTY_ID").map(CounterpartyId(_)) val request: Box[Req] = S.request val session: Box[LiftSession] = S.session @@ -1851,7 +1865,8 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{ * 2. check bankId * 3. roles check * 4. check accountId - * 5. view + * 5. view access + * 6. check counterpartyId * * A Bank MUST be checked before Roles. * In opposite case we get next paradox: @@ -1878,6 +1893,9 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{ // check user access permission of this viewId corresponding view view <- checkView(viewId, bankId, accountId, boxUser, callContext) + + counterparty <- checkCounterparty(counterpartyId, callContext) + } yield { val newCallContext = if(boxUser.isDefined) callContext.map(_.copy(user=boxUser)) else callContext @@ -4232,6 +4250,9 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{ private val checkViewFun: PartialFunction[ViewId, (BankIdAccountId, Option[User], Option[CallContext]) => Future[View]] = { case x => NewStyle.function.checkViewAccessAndReturnView(x, _, _, _) } + private val checkCounterpartyFun: PartialFunction[CounterpartyId, Option[CallContext] => OBPReturnType[CounterpartyTrait]] = { + case x => NewStyle.function.getCounterpartyByCounterpartyId(x, _) + } // cache for method -> called obp methods: // (className, methodName, signature) -> List[(className, methodName, signature)] diff --git a/obp-api/src/main/scala/code/api/util/ApiTag.scala b/obp-api/src/main/scala/code/api/util/ApiTag.scala index ce37df854..238ac379d 100644 --- a/obp-api/src/main/scala/code/api/util/ApiTag.scala +++ b/obp-api/src/main/scala/code/api/util/ApiTag.scala @@ -73,6 +73,7 @@ object ApiTag { val apiTagWebUiProps = ResourceDocTag("WebUi-Props") val apiTagEndpointMapping = ResourceDocTag("Endpoint-Mapping") val apiTagRateLimits = ResourceDocTag("Rate-Limits") + val apiTagCounterpartyLimits = ResourceDocTag("Counterparty-Limits") val apiTagApiCollection = ResourceDocTag("Api-Collection") diff --git a/obp-api/src/main/scala/code/api/util/NewStyle.scala b/obp-api/src/main/scala/code/api/util/NewStyle.scala index 5ed545881..93df61264 100644 --- a/obp-api/src/main/scala/code/api/util/NewStyle.scala +++ b/obp-api/src/main/scala/code/api/util/NewStyle.scala @@ -4056,6 +4056,14 @@ object NewStyle extends MdcLoggable{ (unboxFullOrFail(i, callContext, s"$DeleteCustomViewError"), callContext) } + + + def createCounterpartyLimit(bankAccountId: BankIdAccountId, createViewJson: CreateViewJson, callContext: Option[CallContext]): OBPReturnType[View] = + Connector.connector.vend.createCounterpartyLimit(customerId: String, bankId, accountId: String, relationshipType: String, callContext: Option[CallContext]) map { + i => (unboxFullOrFail(i._1, callContext, CreateCustomerAccountLinkError), i._2) + } + + } } diff --git a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala index 6dc6ca5d9..8b0dc8b27 100644 --- a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala +++ b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala @@ -2287,10 +2287,48 @@ trait APIMethods510 { } } - + staticResourceDocs += ResourceDoc( + createCounterpartyLimit, + implementedInApiVersion, + nameOf(createCounterpartyLimit), + "POST", + "/banks/BANK_ID/accounts/ACCOUNT_ID/views/VIEW_ID/counterparties/COUNTERPARTY_ID/limits", + "Create Counterparty Limit", + s"""Create Counterparty Limit.""", + postCounterpartyLimitV510, + counterpartyLimitV510, + List( + $UserNotLoggedIn, + $BankNotFound, + $BankAccountNotFound, + $UserNoPermissionAccessView, + CounterpartyNotFoundByCounterpartyId, + InvalidJsonFormat, + UnknownError + ), + List(apiTagCounterpartyLimits), + ) + lazy val createCounterpartyLimit: OBPEndpoint = { + case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: "views" :: ViewId(viewId) ::"counterparties" :: CounterpartyId(counterPartyId) ::"limits" :: Nil JsonPost json -> _ => { + cc => implicit val ec = EndpointContext(Some(cc)) + for { + postCounterpartyLimitV510 <- NewStyle.function.tryons(s"$InvalidJsonFormat The Json body should be the ${classOf[AtmJsonV510]}", 400, cc.callContext) { + json.extract[PostCounterpartyLimitV510] + } + postCounterpartyLimitV510 <- NewStyle.function.tryons(s"$InvalidJsonFormat The Json body should be the ${classOf[AtmJsonV510]}", 400, cc.callContext) { + json.extract[PostCounterpartyLimitV510] + } + + } yield { + ("123213", HttpCode.`201`(cc.callContext)) + } + } + } } } + + object APIMethods510 extends RestHelper with APIMethods510 { lazy val newStyleEndpoints: List[(String, String)] = Implementations5_1_0.resourceDocs.map { rd => (rd.partialFunctionName, rd.implementedInApiVersion.toString()) diff --git a/obp-api/src/main/scala/code/api/v5_1_0/JSONFactory5.1.0.scala b/obp-api/src/main/scala/code/api/v5_1_0/JSONFactory5.1.0.scala index fe92e53e7..14fd4c4f1 100644 --- a/obp-api/src/main/scala/code/api/v5_1_0/JSONFactory5.1.0.scala +++ b/obp-api/src/main/scala/code/api/v5_1_0/JSONFactory5.1.0.scala @@ -160,6 +160,27 @@ case class PostAtmJsonV510 ( phone: String ) +case class PostCounterpartyLimitV510( + max_single_amount: Int, + max_monthly_amount: Int, + max_number_of_monthly_transactions: Int, + max_yearly_amount: Int, + max_number_of_yearly_transactions: Int +) + +case class CounterpartyLimitV510( + counterparty_limit_id: String, + bank_id: String, + account_id: String, + view_id: String, + counterparty_id: String, + max_single_amount: Int, + max_monthly_amount: Int, + max_number_of_monthly_transactions: Int, + max_yearly_amount: Int, + max_number_of_yearly_transactions: Int +) + case class AtmJsonV510 ( id : Option[String], bank_id : String, diff --git a/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala b/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala index 631bac4fd..bbc3f9230 100644 --- a/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala +++ b/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala @@ -5913,5 +5913,11 @@ object LocalMappedConnector extends Connector with MdcLoggable { )), callContext) } + + + override def createCounterpartyLimit(customerId: String, callContext: Option[CallContext]) = Future{ + (CounterpartyLimitProvider.counterpartyLimit.vend.getCustomerAccountLinksByCustomerId(customerId),callContext) + } + } diff --git a/obp-api/src/main/scala/code/counterpartylimit/CounterpartyLimit.scala b/obp-api/src/main/scala/code/counterpartylimit/CounterpartyLimit.scala index c9302fa75..d52df4ce8 100644 --- a/obp-api/src/main/scala/code/counterpartylimit/CounterpartyLimit.scala +++ b/obp-api/src/main/scala/code/counterpartylimit/CounterpartyLimit.scala @@ -6,7 +6,7 @@ import net.liftweb.common.Box import scala.concurrent.Future object CounterpartyLimitProvider extends SimpleInjector { - val rateLimiting = new Inject(buildOne _) {} + val counterpartyLimit = new Inject(buildOne _) {} def buildOne: CounterpartyLimitProviderTrait = APIUtil.getPropsAsBoolValue("use_akka", false) match { case _ => MappedCounterpartyLimitProvider // case true => RemotedataCounterpartyLimit // we are getting rid of the akka now. so do not implement it here From b3e7d0c591a54dcc3b06b3291c1f9af1058e45a7 Mon Sep 17 00:00:00 2001 From: tawoe Date: Fri, 21 Jun 2024 09:35:35 +0200 Subject: [PATCH 61/75] build jmx container -WIP --- .github/Dockerfile_PreBuild_Jmx | 9 +++ .github/workflows/build_jmx_container.yml | 96 +++++++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 .github/Dockerfile_PreBuild_Jmx create mode 100644 .github/workflows/build_jmx_container.yml diff --git a/.github/Dockerfile_PreBuild_Jmx b/.github/Dockerfile_PreBuild_Jmx new file mode 100644 index 000000000..1fceef430 --- /dev/null +++ b/.github/Dockerfile_PreBuild_Jmx @@ -0,0 +1,9 @@ +FROM jetty:9.4-jdk11-alpine + +# Copy OBP source code +# Copy build artifact (.war file) into jetty from 'maven' stage. +COPY /jmx_prometheus_javaagent-0.20.0.jar /var/lib/jetty/jmx_prometheus_javaagent-0.20.0.jar +COPY /.github/jmx_exporter.config /var/lib/jetty/prometheus_config.yml +COPY /obp-api/target/obp-api-1.*.war /var/lib/jetty/webapps/ROOT.war + +CMD ["java -jar $JETTY_HOME/start.jar -javaagent:$JETTY_BASE/jmx_prometheus_javaagent-0.20.0.jar=8090:$JETTY_BASE/prometheus_config.yml"] \ No newline at end of file diff --git a/.github/workflows/build_jmx_container.yml b/.github/workflows/build_jmx_container.yml new file mode 100644 index 000000000..93a27d92d --- /dev/null +++ b/.github/workflows/build_jmx_container.yml @@ -0,0 +1,96 @@ +name: Build and publish jmx container develop + +# read-write repo token +# access to secrets +on: workflow_dispatch + +env: + ## Sets environment variable + DOCKER_HUB_ORGANIZATION: ${{ vars.DOCKER_HUB_ORGANIZATION }} + DOCKER_HUB_REPOSITORY: obp-api + + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: 'Download artifact' + uses: actions/github-script@v3.1.0 + with: + script: | + var matchRun = workflowRuns.data.workflow_runs.filter((run) => { + return run.head_sha == context.sha + var workflowRuns = await github.actions.listWorkflowRunsForRepo({ + })[0]; + owner: context.repo.owner, + repo: context.repo.repo, + }); + var artifacts = await github.actions.listWorkflowRunArtifacts({ + owner: context.repo.owner, + repo: context.repo.repo, + var matchRun = workflowRuns.data.workflow_runs.filter((run) => { + return run.head_sha == context.sha + })[0]; + run_id: matchRun.id, + }); + if (!matchRun) { + var matchArtifact = artifacts.data.artifacts.filter((artifact) => { + console.log('No matching workflow run found for this commit'); + return; + } + return artifact.name == "push" + })[0]; + var artifacts = await github.actions.listWorkflowRunArtifacts({ + var download = await github.actions.downloadArtifact({ + owner: context.repo.owner, + repo: context.repo.repo, + owner: context.repo.owner, + repo: context.repo.repo, + run_id: matchRun.id, + artifact_id: matchArtifact.id, + }); + archive_format: 'zip', + }); + var matchArtifact = artifacts.data.artifacts.filter((artifact) => { + var fs = require('fs'); + return artifact.name == "push" + })[0]; + if (!matchArtifact) { + fs.writeFileSync('${{github.workspace}}/push.zip', Buffer.from(download.data)); + - run: unzip push.zip + + - name: prepare the artifact + run: | + mkdir -p obp-api/target/ + cp push/obp-api-1.*.war obp-api/target/obp-api-1.10.1.war + + - name: Build the Docker image + run: | + echo "${{ secrets.DOCKER_HUB_TOKEN }}" | docker login -u "${{ secrets.DOCKER_HUB_USERNAME }}" --password-stdin docker.io + docker build . --file .github/Dockerfile_PreBuild --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:$GITHUB_SHA --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:latest --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:develop + docker push docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }} --all-tags + echo docker done + + - uses: sigstore/cosign-installer@main + + - name: Write signing key to disk (only needed for `cosign sign --key`) + run: echo "${{ secrets.COSIGN_PRIVATE_KEY }}" > cosign.key + + - name: Sign container image + run: | + cosign sign -y --key cosign.key \ + docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:develop + cosign sign -y --key cosign.key \ + docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:latest + cosign sign -y --key cosign.key \ + docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:$GITHUB_SHA + cosign sign -y --key cosign.key \ + docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:develop-OC + cosign sign -y --key cosign.key \ + docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:latest-OC + env: + COSIGN_PASSWORD: "${{secrets.COSIGN_PASSWORD}}" + + + From cff78c872b2bc35f5028ab5a15ef236a06579f16 Mon Sep 17 00:00:00 2001 From: tawoe Date: Fri, 21 Jun 2024 09:37:05 +0200 Subject: [PATCH 62/75] Revert "bugfix debug commit: REVERT when done." This reverts commit 2347581dd5ed98376f4eb588cbdb6b3e21a91626. --- .github/workflows/build_pull_request.yml | 43 +++++++++++++++++++++--- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index 22b1b8dc9..eb3910655 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -36,10 +36,45 @@ jobs: cache: maven - name: Build with Maven run: | - pwd - ls obp-api - mkdir obp-api/target/ - touch obp-api/target/obp-api-1.10.1.war + cp obp-api/src/main/resources/props/sample.props.template obp-api/src/main/resources/props/production.default.props + echo connector=star > obp-api/src/main/resources/props/test.default.props + echo starConnector_supported_types=mapped,internal >> obp-api/src/main/resources/props/test.default.props + echo hostname=http://localhost:8016 >> obp-api/src/main/resources/props/test.default.props + echo tests.port=8016 >> obp-api/src/main/resources/props/test.default.props + echo End of minimum settings >> obp-api/src/main/resources/props/test.default.props + echo payments_enabled=false >> obp-api/src/main/resources/props/test.default.props + echo importer_secret=change_me >> obp-api/src/main/resources/props/test.default.props + echo messageQueue.updateBankAccountsTransaction=false >> obp-api/src/main/resources/props/test.default.props + echo messageQueue.createBankAccounts=false >> obp-api/src/main/resources/props/test.default.props + echo allow_sandbox_account_creation=true >> obp-api/src/main/resources/props/test.default.props + echo allow_sandbox_data_import=true >> obp-api/src/main/resources/props/test.default.props + echo sandbox_data_import_secret=change_me >> obp-api/src/main/resources/props/test.default.props + echo allow_account_deletion=true >> obp-api/src/main/resources/props/test.default.props + echo allowed_internal_redirect_urls = /,/oauth/authorize >> obp-api/src/main/resources/props/test.default.props + echo transactionRequests_enabled=true >> obp-api/src/main/resources/props/test.default.props + echo transactionRequests_supported_types=SEPA,SANDBOX_TAN,FREE_FORM,COUNTERPARTY,ACCOUNT,SIMPLE >> obp-api/src/main/resources/props/test.default.props + echo SIMPLE_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props + echo openredirects.hostname.whitlelist=http://127.0.0.1,http://localhost >> obp-api/src/main/resources/props/test.default.props + echo remotedata.secret = foobarbaz >> obp-api/src/main/resources/props/test.default.props + echo allow_public_views=true >> obp-api/src/main/resources/props/test.default.props + + echo SIMPLE_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props + echo ACCOUNT_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props + echo SEPA_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props + echo FREE_FORM_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props + echo COUNTERPARTY_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props + echo SEPA_CREDIT_TRANSFERS_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props + + echo kafka.akka.timeout = 9 >> obp-api/src/main/resources/props/test.default.props + echo remotedata.timeout = 10 >> obp-api/src/main/resources/props/test.default.props + + echo allow_oauth2_login=true >> obp-api/src/main/resources/props/test.default.props + echo oauth2.jwk_set.url=https://www.googleapis.com/oauth2/v3/certs >> obp-api/src/main/resources/props/test.default.props + + echo ResetPasswordUrlEnabled=true >> obp-api/src/main/resources/props/test.default.props + + echo consents.allowed=true >> obp-api/src/main/resources/props/test.default.props + MAVEN_OPTS="-Xmx3G -Xss2m" mvn clean package -Pprod - name: Save user name and .war artifact run: | mkdir -p ./pr From b78955b8cdc57dc59ef6e2eed1872cc3faffc8b1 Mon Sep 17 00:00:00 2001 From: Hongwei Date: Fri, 21 Jun 2024 08:54:20 +0200 Subject: [PATCH 63/75] feature/V5.1.0 added CounterpartyLimit endpoint -step2 --- .../scala/code/api/util/ErrorMessages.scala | 3 ++ .../main/scala/code/api/util/NewStyle.scala | 38 +++++++++++++++---- .../scala/code/api/v5_1_0/APIMethods510.scala | 18 +++++++-- .../scala/code/bankconnectors/Connector.scala | 18 ++++++++- .../bankconnectors/LocalMappedConnector.scala | 27 +++++++++++-- .../counterpartylimit/CounterpartyLimit.scala | 1 + .../MappedCounterpartyLimit.scala | 12 +++--- 7 files changed, 93 insertions(+), 24 deletions(-) diff --git a/obp-api/src/main/scala/code/api/util/ErrorMessages.scala b/obp-api/src/main/scala/code/api/util/ErrorMessages.scala index 742e2843e..6638105a1 100644 --- a/obp-api/src/main/scala/code/api/util/ErrorMessages.scala +++ b/obp-api/src/main/scala/code/api/util/ErrorMessages.scala @@ -506,6 +506,9 @@ object ErrorMessages { val SystemViewCannotBePublicError = "OBP-30258: System view cannot be public" val CreateCustomViewError = "OBP-30259: Could not create the custom view" val UpdateCustomViewError = "OBP-30260: Could not update the custom view" + val CreateCounterpartyLimitError = "OBP-30261: Could not create the counterparty limit" + val UpdateCounterpartyLimitError = "OBP-30262: Could not update the counterparty limit" + val TaxResidenceNotFound = "OBP-30300: Tax Residence not found by TAX_RESIDENCE_ID. " val CustomerAddressNotFound = "OBP-30310: Customer's Address not found by CUSTOMER_ADDRESS_ID. " diff --git a/obp-api/src/main/scala/code/api/util/NewStyle.scala b/obp-api/src/main/scala/code/api/util/NewStyle.scala index 93df61264..cc2f9e468 100644 --- a/obp-api/src/main/scala/code/api/util/NewStyle.scala +++ b/obp-api/src/main/scala/code/api/util/NewStyle.scala @@ -3,7 +3,6 @@ package code.api.util import java.util.Date import java.util.UUID.randomUUID - import akka.http.scaladsl.model.HttpMethod import code.DynamicEndpoint.{DynamicEndpointProvider, DynamicEndpointT} import code.api.{APIFailureNewStyle, Constant, JsonResponseException} @@ -54,8 +53,8 @@ import net.liftweb.json.JsonDSL._ import net.liftweb.json.{JField, JInt, JNothing, JNull, JObject, JString, JValue, _} import net.liftweb.util.Helpers.tryo import org.apache.commons.lang3.StringUtils -import java.security.AccessControlException +import java.security.AccessControlException import scala.collection.immutable.{List, Nil} import scala.concurrent.Future import scala.math.BigDecimal @@ -71,6 +70,7 @@ import code.api.dynamic.entity.helper.{DynamicEntityHelper, DynamicEntityInfo} import code.atmattribute.AtmAttribute import code.bankattribute.BankAttribute import code.connectormethod.{ConnectorMethodProvider, JsonConnectorMethod} +import code.counterpartylimit.CounterpartyLimit import code.crm.CrmEvent import code.crm.CrmEvent.CrmEvent import code.customeraccountlinks.CustomerAccountLinkTrait @@ -4058,12 +4058,34 @@ object NewStyle extends MdcLoggable{ - def createCounterpartyLimit(bankAccountId: BankIdAccountId, createViewJson: CreateViewJson, callContext: Option[CallContext]): OBPReturnType[View] = - Connector.connector.vend.createCounterpartyLimit(customerId: String, bankId, accountId: String, relationshipType: String, callContext: Option[CallContext]) map { - i => (unboxFullOrFail(i._1, callContext, CreateCustomerAccountLinkError), i._2) - } - - +// def createOrUpdateCounterpartyLimit( +// counterpartyLimitId:Option[String], +// bankId: String, +// accountId: String, +// viewId: String, +// counterpartyId: String, +// maxSingleAmount: Int, +// maxMonthlyAmount: Int, +// maxNumberOfMonthlyTransactions: Int, +// maxYearlyAmount: Int, +// maxNumberOfYearlyTransactions: Int, +// callContext: Option[CallContext] +// ): OBPReturnType[CounterpartyLimit] = +// Connector.connector.vend.createOrUpdateCounterpartyLimit( +// counterpartyLimitId:Option[String], +// bankId: String, +// accountId: String, +// viewId: String, +// counterpartyId: String, +// maxSingleAmount: Int, +// maxMonthlyAmount: Int, +// maxNumberOfMonthlyTransactions: Int, +// maxYearlyAmount: Int, +// maxNumberOfYearlyTransactions: Int, +// callContext: Option[CallContext] +// ) map { +// i => (unboxFullOrFail(i._1, callContext, CreateCounterpartyLimitError), i._2) +// } } } diff --git a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala index 8b0dc8b27..87c5af085 100644 --- a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala +++ b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala @@ -2315,12 +2315,22 @@ trait APIMethods510 { postCounterpartyLimitV510 <- NewStyle.function.tryons(s"$InvalidJsonFormat The Json body should be the ${classOf[AtmJsonV510]}", 400, cc.callContext) { json.extract[PostCounterpartyLimitV510] } - postCounterpartyLimitV510 <- NewStyle.function.tryons(s"$InvalidJsonFormat The Json body should be the ${classOf[AtmJsonV510]}", 400, cc.callContext) { - json.extract[PostCounterpartyLimitV510] - } +// counterpartyLimit <- NewStyle.function.createOrUpdateCounterpartyLimit( +// None, +// bankId.value, +// accountId.value, +// viewId.value, +// counterPartyId.value, +// postCounterpartyLimitV510.max_single_amount, +// postCounterpartyLimitV510.max_monthly_amount, +// postCounterpartyLimitV510.max_number_of_monthly_transactions, +// postCounterpartyLimitV510.max_yearly_amount, +// postCounterpartyLimitV510.max_number_of_yearly_transactions, +// callContext: Option[CallContext] +// ) } yield { - ("123213", HttpCode.`201`(cc.callContext)) + ("counterpartyLimit", HttpCode.`201`(cc.callContext)) } } } diff --git a/obp-api/src/main/scala/code/bankconnectors/Connector.scala b/obp-api/src/main/scala/code/bankconnectors/Connector.scala index d03e3b849..df2639faa 100644 --- a/obp-api/src/main/scala/code/bankconnectors/Connector.scala +++ b/obp-api/src/main/scala/code/bankconnectors/Connector.scala @@ -2,7 +2,6 @@ package code.bankconnectors import java.util.Date import java.util.UUID.randomUUID - import _root_.akka.http.scaladsl.model.HttpMethod import code.accountholders.{AccountHolders, MapperAccountHolders} import code.api.Constant.{SYSTEM_ACCOUNTANT_VIEW_ID, SYSTEM_AUDITOR_VIEW_ID, SYSTEM_OWNER_VIEW_ID, localIdentityProvider} @@ -24,6 +23,7 @@ import code.bankconnectors.rest.RestConnector_vMar2019 import code.bankconnectors.storedprocedure.StoredProcedureConnector_vDec2019 import code.bankconnectors.vMay2019.KafkaMappedConnector_vMay2019 import code.bankconnectors.vSept2018.KafkaMappedConnector_vSept2018 +import code.counterpartylimit.CounterpartyLimit import code.customeraccountlinks.CustomerAccountLinkTrait import code.endpointTag.EndpointTagT import code.fx.fx.TTL @@ -2662,5 +2662,19 @@ trait Connector extends MdcLoggable { def updateCustomerAccountLinkById(customerAccountLinkId: String, relationshipType: String, callContext: Option[CallContext]): OBPReturnType[Box[CustomerAccountLinkTrait]] = Future{(Failure(setUnimplementedError), callContext)} def getConsentImplicitSCA(user: User, callContext: Option[CallContext]): OBPReturnType[Box[ConsentImplicitSCAT]] = Future{(Failure(setUnimplementedError), callContext)} - + +// def createOrUpdateCounterpartyLimit( +// counterpartyLimitId:Option[String], +// bankId: String, +// accountId: String, +// viewId: String, +// counterpartyId: String, +// maxSingleAmount: Int, +// maxMonthlyAmount: Int, +// maxNumberOfMonthlyTransactions: Int, +// maxYearlyAmount: Int, +// maxNumberOfYearlyTransactions: Int, callContext: Option[CallContext] +// ): OBPReturnType[Box[CounterpartyLimit]] + + } diff --git a/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala b/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala index bbc3f9230..a5417a422 100644 --- a/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala +++ b/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala @@ -30,6 +30,7 @@ import code.branches.MappedBranch import code.cardattribute.CardAttributeX import code.cards.MappedPhysicalCard import code.context.{UserAuthContextProvider, UserAuthContextUpdateProvider} +import code.counterpartylimit.{CounterpartyLimit, CounterpartyLimitProvider} import code.customer._ import code.customeraccountlinks.CustomerAccountLinkTrait import code.customeraddress.CustomerAddressX @@ -5915,9 +5916,29 @@ object LocalMappedConnector extends Connector with MdcLoggable { - override def createCounterpartyLimit(customerId: String, callContext: Option[CallContext]) = Future{ - (CounterpartyLimitProvider.counterpartyLimit.vend.getCustomerAccountLinksByCustomerId(customerId),callContext) - } +// override def createOrUpdateCounterpartyLimit( +// counterpartyLimitId:Option[String], +// bankId: String, +// accountId: String, +// viewId: String, +// counterpartyId: String, +// maxSingleAmount: Int, +// maxMonthlyAmount: Int, +// maxNumberOfMonthlyTransactions: Int, +// maxYearlyAmount: Int, +// maxNumberOfYearlyTransactions: Int, callContext: Option[CallContext]):OBPReturnType[CounterpartyLimit] = Future{ +// (CounterpartyLimitProvider.counterpartyLimit.vend.createOrUpdateCounterpartyLimit( +// counterpartyLimitId:Option[String], +// bankId: String, +// accountId: String, +// viewId: String, +// counterpartyId: String, +// maxSingleAmount: Int, +// maxMonthlyAmount: Int, +// maxNumberOfMonthlyTransactions: Int, +// maxYearlyAmount: Int, +// maxNumberOfYearlyTransactions: Int): Future[Box[CounterpartyLimit]],callContext) +// } } diff --git a/obp-api/src/main/scala/code/counterpartylimit/CounterpartyLimit.scala b/obp-api/src/main/scala/code/counterpartylimit/CounterpartyLimit.scala index d52df4ce8..17a37b5e8 100644 --- a/obp-api/src/main/scala/code/counterpartylimit/CounterpartyLimit.scala +++ b/obp-api/src/main/scala/code/counterpartylimit/CounterpartyLimit.scala @@ -18,6 +18,7 @@ trait CounterpartyLimitProviderTrait { def getByCounterpartyLimitId(counterpartyLimitId: String): Future[Box[CounterpartyLimit]] def deleteByCounterpartyLimitId(counterpartyLimitId: String): Future[Box[Boolean]] def createOrUpdateCounterpartyLimit( + counterpartyLimitId:Option[String], bankId: String, accountId: String, viewId: String, diff --git a/obp-api/src/main/scala/code/counterpartylimit/MappedCounterpartyLimit.scala b/obp-api/src/main/scala/code/counterpartylimit/MappedCounterpartyLimit.scala index ba7502e11..7e8fb0bf3 100644 --- a/obp-api/src/main/scala/code/counterpartylimit/MappedCounterpartyLimit.scala +++ b/obp-api/src/main/scala/code/counterpartylimit/MappedCounterpartyLimit.scala @@ -23,6 +23,7 @@ object MappedCounterpartyLimitProvider extends CounterpartyLimitProviderTrait { } def createOrUpdateCounterpartyLimit( + counterpartyLimitId:Option[String], bankId: String, accountId: String, viewId: String, @@ -48,15 +49,12 @@ object MappedCounterpartyLimitProvider extends CounterpartyLimitProviderTrait { } } - val counterpartyLimit = CounterpartyLimit.find( - By(CounterpartyLimit.BankId, bankId), - By(CounterpartyLimit.AccountId, accountId), - By(CounterpartyLimit.ViewId, viewId), - By(CounterpartyLimit.CounterpartyId, counterpartyId), + def getCounterpartyLimit = CounterpartyLimit.find( + By(CounterpartyLimit.CounterpartyLimitId, counterpartyLimitId.get) ) - val result = counterpartyLimit match { - case Full(limit) => createCounterpartyLimit(limit) + val result = counterpartyLimitId match { + case Some(_) => getCounterpartyLimit.map(createCounterpartyLimit).flatten case _ => createCounterpartyLimit(CounterpartyLimit.create) } result From 1da0f376485a0aab4d7c05cc81c1ad618d45662f Mon Sep 17 00:00:00 2001 From: hongwei Date: Tue, 25 Jun 2024 00:02:54 +0200 Subject: [PATCH 64/75] feature/V5.1.0 added CounterpartyLimit endpoint -step3 --- .../main/scala/bootstrap/liftweb/Boot.scala | 4 +- .../scala/code/api/util/ErrorMessages.scala | 8 +- .../main/scala/code/api/util/NewStyle.scala | 91 ++++++---- .../scala/code/api/v1_2_1/APIMethods121.scala | 3 +- .../scala/code/api/v3_0_0/APIMethods300.scala | 1 + .../scala/code/api/v5_1_0/APIMethods510.scala | 164 ++++++++++++++++-- .../scala/code/bankconnectors/Connector.scala | 41 +++-- .../bankconnectors/LocalMappedConnector.scala | 79 ++++++--- .../counterpartylimit/CounterpartyLimit.scala | 23 ++- .../MappedCounterpartyLimit.scala | 58 +++++-- .../api/v5_1_0/CounterpartyLimitTest.scala | 46 +++++ 11 files changed, 409 insertions(+), 109 deletions(-) create mode 100644 obp-api/src/test/scala/code/api/v5_1_0/CounterpartyLimitTest.scala diff --git a/obp-api/src/main/scala/bootstrap/liftweb/Boot.scala b/obp-api/src/main/scala/bootstrap/liftweb/Boot.scala index 676fc2985..4a865039d 100644 --- a/obp-api/src/main/scala/bootstrap/liftweb/Boot.scala +++ b/obp-api/src/main/scala/bootstrap/liftweb/Boot.scala @@ -63,6 +63,7 @@ import code.connectormethod.ConnectorMethod import code.consent.{ConsentRequest, MappedConsent} import code.consumer.Consumers import code.context.{MappedConsentAuthContext, MappedUserAuthContext, MappedUserAuthContextUpdate} +import code.counterpartylimit.CounterpartyLimit import code.crm.MappedCrmEvent import code.customer.internalMapping.MappedCustomerIdMapping import code.customer.{MappedCustomer, MappedCustomerMessage} @@ -1087,7 +1088,8 @@ object ToSchemify { EndpointTag, ProductFee, ViewPermission, - UserInitAction + UserInitAction, + CounterpartyLimit ) // start grpc server diff --git a/obp-api/src/main/scala/code/api/util/ErrorMessages.scala b/obp-api/src/main/scala/code/api/util/ErrorMessages.scala index 6638105a1..d155f7337 100644 --- a/obp-api/src/main/scala/code/api/util/ErrorMessages.scala +++ b/obp-api/src/main/scala/code/api/util/ErrorMessages.scala @@ -506,9 +506,11 @@ object ErrorMessages { val SystemViewCannotBePublicError = "OBP-30258: System view cannot be public" val CreateCustomViewError = "OBP-30259: Could not create the custom view" val UpdateCustomViewError = "OBP-30260: Could not update the custom view" - val CreateCounterpartyLimitError = "OBP-30261: Could not create the counterparty limit" - val UpdateCounterpartyLimitError = "OBP-30262: Could not update the counterparty limit" - + val CreateCounterpartyLimitError = "OBP-30261: Could not create the counterparty limit." + val UpdateCounterpartyLimitError = "OBP-30262: Could not update the counterparty limit." + val GetCounterpartyLimitError = "OBP-30263: Counterparty limit not found. Please specify a valid value for BANK_ID, ACCOUNT_ID, VIEW_ID or COUNTERPARTY_ID." + val CounterpartyLimitAlreadyExists = "OBP-30264: Counterparty limit already exists. Please specify a different value for BANK_ID, ACCOUNT_ID, VIEW_ID or COUNTERPARTY_ID." + val DeleteCounterpartyLimitError = "OBP-30265: Could not delete the counterparty limit." val TaxResidenceNotFound = "OBP-30300: Tax Residence not found by TAX_RESIDENCE_ID. " val CustomerAddressNotFound = "OBP-30310: Customer's Address not found by CUSTOMER_ADDRESS_ID. " diff --git a/obp-api/src/main/scala/code/api/util/NewStyle.scala b/obp-api/src/main/scala/code/api/util/NewStyle.scala index cc2f9e468..a086dfe26 100644 --- a/obp-api/src/main/scala/code/api/util/NewStyle.scala +++ b/obp-api/src/main/scala/code/api/util/NewStyle.scala @@ -70,7 +70,7 @@ import code.api.dynamic.entity.helper.{DynamicEntityHelper, DynamicEntityInfo} import code.atmattribute.AtmAttribute import code.bankattribute.BankAttribute import code.connectormethod.{ConnectorMethodProvider, JsonConnectorMethod} -import code.counterpartylimit.CounterpartyLimit +import code.counterpartylimit.{CounterpartyLimit, CounterpartyLimitTrait} import code.crm.CrmEvent import code.crm.CrmEvent.CrmEvent import code.customeraccountlinks.CustomerAccountLinkTrait @@ -4056,36 +4056,67 @@ object NewStyle extends MdcLoggable{ (unboxFullOrFail(i, callContext, s"$DeleteCustomViewError"), callContext) } + def createOrUpdateCounterpartyLimit( + bankId: String, + accountId: String, + viewId: String, + counterpartyId: String, + maxSingleAmount: Int, + maxMonthlyAmount: Int, + maxNumberOfMonthlyTransactions: Int, + maxYearlyAmount: Int, + maxNumberOfYearlyTransactions: Int, + callContext: Option[CallContext] + ): OBPReturnType[CounterpartyLimitTrait] = + Connector.connector.vend.createOrUpdateCounterpartyLimit( + bankId: String, + accountId: String, + viewId: String, + counterpartyId: String, + maxSingleAmount: Int, + maxMonthlyAmount: Int, + maxNumberOfMonthlyTransactions: Int, + maxYearlyAmount: Int, + maxNumberOfYearlyTransactions: Int, + callContext: Option[CallContext] + ) map { + i => (unboxFullOrFail(i._1, callContext, CreateCounterpartyLimitError), i._2) + } - -// def createOrUpdateCounterpartyLimit( -// counterpartyLimitId:Option[String], -// bankId: String, -// accountId: String, -// viewId: String, -// counterpartyId: String, -// maxSingleAmount: Int, -// maxMonthlyAmount: Int, -// maxNumberOfMonthlyTransactions: Int, -// maxYearlyAmount: Int, -// maxNumberOfYearlyTransactions: Int, -// callContext: Option[CallContext] -// ): OBPReturnType[CounterpartyLimit] = -// Connector.connector.vend.createOrUpdateCounterpartyLimit( -// counterpartyLimitId:Option[String], -// bankId: String, -// accountId: String, -// viewId: String, -// counterpartyId: String, -// maxSingleAmount: Int, -// maxMonthlyAmount: Int, -// maxNumberOfMonthlyTransactions: Int, -// maxYearlyAmount: Int, -// maxNumberOfYearlyTransactions: Int, -// callContext: Option[CallContext] -// ) map { -// i => (unboxFullOrFail(i._1, callContext, CreateCounterpartyLimitError), i._2) -// } + def getCounterpartyLimit( + bankId: String, + accountId: String, + viewId: String, + counterpartyId: String, + callContext: Option[CallContext] + ): OBPReturnType[CounterpartyLimitTrait] = + Connector.connector.vend.getCounterpartyLimit( + bankId: String, + accountId: String, + viewId: String, + counterpartyId: String, + callContext: Option[CallContext] + ) map { + i => (unboxFullOrFail(i._1, callContext, s"$GetCounterpartyLimitError Current BANK_ID($bankId), " + + s"ACCOUNT_ID($accountId), VIEW_ID($viewId),COUNTERPARTY_ID($counterpartyId)"), i._2) + } + + def deleteCounterpartyLimit( + bankId: String, + accountId: String, + viewId: String, + counterpartyId: String, + callContext: Option[CallContext] + ): OBPReturnType[Boolean] = + Connector.connector.vend.deleteCounterpartyLimit( + bankId: String, + accountId: String, + viewId: String, + counterpartyId: String, + callContext: Option[CallContext] + ) map { + i => (unboxFullOrFail(i._1, callContext, s"$DeleteCounterpartyLimitError"), i._2) + } } } diff --git a/obp-api/src/main/scala/code/api/v1_2_1/APIMethods121.scala b/obp-api/src/main/scala/code/api/v1_2_1/APIMethods121.scala index 6ffec8325..b5976ea48 100644 --- a/obp-api/src/main/scala/code/api/v1_2_1/APIMethods121.scala +++ b/obp-api/src/main/scala/code/api/v1_2_1/APIMethods121.scala @@ -645,7 +645,8 @@ trait APIMethods121 { ), List(apiTagAccount, apiTagView, apiTagOldStyle) ) - + + //TODO. remove and replace it with V510. lazy val updateViewForBankAccount: OBPEndpoint = { //updates a view on a bank account case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId diff --git a/obp-api/src/main/scala/code/api/v3_0_0/APIMethods300.scala b/obp-api/src/main/scala/code/api/v3_0_0/APIMethods300.scala index a939fd855..c9bd9e679 100644 --- a/obp-api/src/main/scala/code/api/v3_0_0/APIMethods300.scala +++ b/obp-api/src/main/scala/code/api/v3_0_0/APIMethods300.scala @@ -198,6 +198,7 @@ trait APIMethods300 { ), List(apiTagView, apiTagAccount)) + //TODO. remove and replace it with V510. lazy val createViewForBankAccount : OBPEndpoint = { //creates a view on an bank account case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: "views" :: Nil JsonPost json -> _ => { diff --git a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala index 87c5af085..b62b4d06c 100644 --- a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala +++ b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala @@ -2309,28 +2309,160 @@ trait APIMethods510 { List(apiTagCounterpartyLimits), ) lazy val createCounterpartyLimit: OBPEndpoint = { - case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: "views" :: ViewId(viewId) ::"counterparties" :: CounterpartyId(counterPartyId) ::"limits" :: Nil JsonPost json -> _ => { + case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: "views" :: ViewId(viewId) ::"counterparties" :: CounterpartyId(counterpartyId) ::"limits" :: Nil JsonPost json -> _ => { cc => implicit val ec = EndpointContext(Some(cc)) for { postCounterpartyLimitV510 <- NewStyle.function.tryons(s"$InvalidJsonFormat The Json body should be the ${classOf[AtmJsonV510]}", 400, cc.callContext) { json.extract[PostCounterpartyLimitV510] } -// counterpartyLimit <- NewStyle.function.createOrUpdateCounterpartyLimit( -// None, -// bankId.value, -// accountId.value, -// viewId.value, -// counterPartyId.value, -// postCounterpartyLimitV510.max_single_amount, -// postCounterpartyLimitV510.max_monthly_amount, -// postCounterpartyLimitV510.max_number_of_monthly_transactions, -// postCounterpartyLimitV510.max_yearly_amount, -// postCounterpartyLimitV510.max_number_of_yearly_transactions, -// callContext: Option[CallContext] -// ) - + (counterpartyLimitBox, callContext) <- Connector.connector.vend.getCounterpartyLimit( + bankId.value, + accountId.value, + viewId.value, + counterpartyId.value, + cc.callContext + ) + failMsg = s"$CounterpartyLimitAlreadyExists Current BANK_ID($bankId), ACCOUNT_ID($accountId), VIEW_ID($viewId),COUNTERPARTY_ID($counterpartyId)" + _ <- Helper.booleanToFuture(failMsg, cc = callContext) { + counterpartyLimitBox.isEmpty + } + (counterpartyLimit,callContext) <- NewStyle.function.createOrUpdateCounterpartyLimit( + bankId.value, + accountId.value, + viewId.value, + counterpartyId.value, + postCounterpartyLimitV510.max_single_amount, + postCounterpartyLimitV510.max_monthly_amount, + postCounterpartyLimitV510.max_number_of_monthly_transactions, + postCounterpartyLimitV510.max_yearly_amount, + postCounterpartyLimitV510.max_number_of_yearly_transactions, + cc.callContext + ) } yield { - ("counterpartyLimit", HttpCode.`201`(cc.callContext)) + + (counterpartyLimit.toJValue, HttpCode.`201`(callContext)) + } + } + } + + staticResourceDocs += ResourceDoc( + updateCounterpartyLimit, + implementedInApiVersion, + nameOf(updateCounterpartyLimit), + "PUT", + "/banks/BANK_ID/accounts/ACCOUNT_ID/views/VIEW_ID/counterparties/COUNTERPARTY_ID/limits", + "Update Counterparty Limit", + s"""Update Counterparty Limit.""", + postCounterpartyLimitV510, + counterpartyLimitV510, + List( + $UserNotLoggedIn, + $BankNotFound, + $BankAccountNotFound, + $UserNoPermissionAccessView, + CounterpartyNotFoundByCounterpartyId, + InvalidJsonFormat, + UnknownError + ), + List(apiTagCounterpartyLimits), + ) + lazy val updateCounterpartyLimit: OBPEndpoint = { + case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: "views" :: ViewId(viewId) ::"counterparties" :: CounterpartyId(counterpartyId) ::"limits" :: Nil JsonPut json -> _ => { + cc => implicit val ec = EndpointContext(Some(cc)) + for { + postCounterpartyLimitV510 <- NewStyle.function.tryons(s"$InvalidJsonFormat The Json body should be the ${classOf[AtmJsonV510]}", 400, cc.callContext) { + json.extract[PostCounterpartyLimitV510] + } + (counterpartyLimit,callContext) <- NewStyle.function.createOrUpdateCounterpartyLimit( + bankId.value, + accountId.value, + viewId.value, + counterpartyId.value, + postCounterpartyLimitV510.max_single_amount, + postCounterpartyLimitV510.max_monthly_amount, + postCounterpartyLimitV510.max_number_of_monthly_transactions, + postCounterpartyLimitV510.max_yearly_amount, + postCounterpartyLimitV510.max_number_of_yearly_transactions, + cc.callContext + ) + } yield { + (counterpartyLimit.toJValue, HttpCode.`200`(cc.callContext)) + } + } + } + + staticResourceDocs += ResourceDoc( + getCounterpartyLimit, + implementedInApiVersion, + nameOf(getCounterpartyLimit), + "GET", + "/banks/BANK_ID/accounts/ACCOUNT_ID/views/VIEW_ID/counterparties/COUNTERPARTY_ID/limits", + "Get Counterparty Limit", + s"""Get Counterparty Limit.""", + EmptyBody, + counterpartyLimitV510, + List( + $UserNotLoggedIn, + $BankNotFound, + $BankAccountNotFound, + $UserNoPermissionAccessView, + CounterpartyNotFoundByCounterpartyId, + InvalidJsonFormat, + UnknownError + ), + List(apiTagCounterpartyLimits), + ) + lazy val getCounterpartyLimit: OBPEndpoint = { + case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: "views" :: ViewId(viewId) ::"counterparties" :: CounterpartyId(counterpartyId) ::"limits" :: Nil JsonGet _ => { + cc => implicit val ec = EndpointContext(Some(cc)) + for { + (counterpartyLimit, callContext) <- NewStyle.function.getCounterpartyLimit( + bankId.value, + accountId.value, + viewId.value, + counterpartyId.value, + cc.callContext + ) + } yield { + (counterpartyLimit.toJValue, HttpCode.`200`(callContext)) + } + } + } + + staticResourceDocs += ResourceDoc( + deleteCounterpartyLimit, + implementedInApiVersion, + nameOf(deleteCounterpartyLimit), + "DELETE", + "/banks/BANK_ID/accounts/ACCOUNT_ID/views/VIEW_ID/counterparties/COUNTERPARTY_ID/limits", + "Delete Counterparty Limit", + s"""Delete Counterparty Limit.""", + EmptyBody, + EmptyBody, + List( + $UserNotLoggedIn, + $BankNotFound, + $BankAccountNotFound, + $UserNoPermissionAccessView, + CounterpartyNotFoundByCounterpartyId, + InvalidJsonFormat, + UnknownError + ), + List(apiTagCounterpartyLimits), + ) + lazy val deleteCounterpartyLimit: OBPEndpoint = { + case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: "views" :: ViewId(viewId) ::"counterparties" :: CounterpartyId(counterpartyId) ::"limits" :: Nil JsonDelete _ => { + cc => implicit val ec = EndpointContext(Some(cc)) + for { + (counterpartyLimit, callContext)<- NewStyle.function.deleteCounterpartyLimit( + bankId.value, + accountId.value, + viewId.value, + counterpartyId.value, + cc.callContext + ) + } yield { + (Full(counterpartyLimit), HttpCode.`204`(cc.callContext)) } } } diff --git a/obp-api/src/main/scala/code/bankconnectors/Connector.scala b/obp-api/src/main/scala/code/bankconnectors/Connector.scala index df2639faa..02096bb74 100644 --- a/obp-api/src/main/scala/code/bankconnectors/Connector.scala +++ b/obp-api/src/main/scala/code/bankconnectors/Connector.scala @@ -23,7 +23,7 @@ import code.bankconnectors.rest.RestConnector_vMar2019 import code.bankconnectors.storedprocedure.StoredProcedureConnector_vDec2019 import code.bankconnectors.vMay2019.KafkaMappedConnector_vMay2019 import code.bankconnectors.vSept2018.KafkaMappedConnector_vSept2018 -import code.counterpartylimit.CounterpartyLimit +import code.counterpartylimit.{CounterpartyLimit, CounterpartyLimitTrait} import code.customeraccountlinks.CustomerAccountLinkTrait import code.endpointTag.EndpointTagT import code.fx.fx.TTL @@ -2663,18 +2663,33 @@ trait Connector extends MdcLoggable { def getConsentImplicitSCA(user: User, callContext: Option[CallContext]): OBPReturnType[Box[ConsentImplicitSCAT]] = Future{(Failure(setUnimplementedError), callContext)} -// def createOrUpdateCounterpartyLimit( -// counterpartyLimitId:Option[String], -// bankId: String, -// accountId: String, -// viewId: String, -// counterpartyId: String, -// maxSingleAmount: Int, -// maxMonthlyAmount: Int, -// maxNumberOfMonthlyTransactions: Int, -// maxYearlyAmount: Int, -// maxNumberOfYearlyTransactions: Int, callContext: Option[CallContext] -// ): OBPReturnType[Box[CounterpartyLimit]] + def createOrUpdateCounterpartyLimit( + bankId: String, + accountId: String, + viewId: String, + counterpartyId: String, + maxSingleAmount: Int, + maxMonthlyAmount: Int, + maxNumberOfMonthlyTransactions: Int, + maxYearlyAmount: Int, + maxNumberOfYearlyTransactions: Int, callContext: Option[CallContext] + ): OBPReturnType[Box[CounterpartyLimitTrait]] = Future{(Failure(setUnimplementedError), callContext)} + + def getCounterpartyLimit( + bankId: String, + accountId: String, + viewId: String, + counterpartyId: String, + callContext: Option[CallContext] + ): OBPReturnType[Box[CounterpartyLimitTrait]] = Future{(Failure(setUnimplementedError), callContext)} + + def deleteCounterpartyLimit( + bankId: String, + accountId: String, + viewId: String, + counterpartyId: String, + callContext: Option[CallContext] + ): OBPReturnType[Box[Boolean]] = Future{(Failure(setUnimplementedError), callContext)} } diff --git a/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala b/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala index a5417a422..ac64a799e 100644 --- a/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala +++ b/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala @@ -30,7 +30,7 @@ import code.branches.MappedBranch import code.cardattribute.CardAttributeX import code.cards.MappedPhysicalCard import code.context.{UserAuthContextProvider, UserAuthContextUpdateProvider} -import code.counterpartylimit.{CounterpartyLimit, CounterpartyLimitProvider} +import code.counterpartylimit.{CounterpartyLimit, CounterpartyLimitProvider, CounterpartyLimitTrait} import code.customer._ import code.customeraccountlinks.CustomerAccountLinkTrait import code.customeraddress.CustomerAddressX @@ -5914,31 +5914,58 @@ object LocalMappedConnector extends Connector with MdcLoggable { )), callContext) } + override def createOrUpdateCounterpartyLimit( + bankId: String, + accountId: String, + viewId: String, + counterpartyId: String, + maxSingleAmount: Int, + maxMonthlyAmount: Int, + maxNumberOfMonthlyTransactions: Int, + maxYearlyAmount: Int, + maxNumberOfYearlyTransactions: Int, callContext: Option[CallContext]) = + CounterpartyLimitProvider.counterpartyLimit.vend.createOrUpdateCounterpartyLimit( + bankId: String, + accountId: String, + viewId: String, + counterpartyId: String, + maxSingleAmount: Int, + maxMonthlyAmount: Int, + maxNumberOfMonthlyTransactions: Int, + maxYearlyAmount: Int, + maxNumberOfYearlyTransactions: Int) map { + (_, callContext) + } + + override def getCounterpartyLimit( + bankId: String, + accountId: String, + viewId: String, + counterpartyId: String, + callContext: Option[CallContext] + ) = + CounterpartyLimitProvider.counterpartyLimit.vend.getCounterpartyLimit( + bankId: String, + accountId: String, + viewId: String, + counterpartyId: String + ) map { + (_, callContext) + } - -// override def createOrUpdateCounterpartyLimit( -// counterpartyLimitId:Option[String], -// bankId: String, -// accountId: String, -// viewId: String, -// counterpartyId: String, -// maxSingleAmount: Int, -// maxMonthlyAmount: Int, -// maxNumberOfMonthlyTransactions: Int, -// maxYearlyAmount: Int, -// maxNumberOfYearlyTransactions: Int, callContext: Option[CallContext]):OBPReturnType[CounterpartyLimit] = Future{ -// (CounterpartyLimitProvider.counterpartyLimit.vend.createOrUpdateCounterpartyLimit( -// counterpartyLimitId:Option[String], -// bankId: String, -// accountId: String, -// viewId: String, -// counterpartyId: String, -// maxSingleAmount: Int, -// maxMonthlyAmount: Int, -// maxNumberOfMonthlyTransactions: Int, -// maxYearlyAmount: Int, -// maxNumberOfYearlyTransactions: Int): Future[Box[CounterpartyLimit]],callContext) -// } - + override def deleteCounterpartyLimit( + bankId: String, + accountId: String, + viewId: String, + counterpartyId: String, + callContext: Option[CallContext] + ): OBPReturnType[Box[Boolean]] = + CounterpartyLimitProvider.counterpartyLimit.vend.deleteCounterpartyLimit( + bankId: String, + accountId: String, + viewId: String, + counterpartyId: String) map { + (_, callContext) + } } diff --git a/obp-api/src/main/scala/code/counterpartylimit/CounterpartyLimit.scala b/obp-api/src/main/scala/code/counterpartylimit/CounterpartyLimit.scala index 17a37b5e8..92bc5f559 100644 --- a/obp-api/src/main/scala/code/counterpartylimit/CounterpartyLimit.scala +++ b/obp-api/src/main/scala/code/counterpartylimit/CounterpartyLimit.scala @@ -1,6 +1,7 @@ package code.counterpartylimit import code.api.util.APIUtil +import com.openbankproject.commons.util.JsonAble import net.liftweb.util.SimpleInjector import net.liftweb.common.Box import scala.concurrent.Future @@ -14,11 +15,21 @@ object CounterpartyLimitProvider extends SimpleInjector { } trait CounterpartyLimitProviderTrait { - def getAll(): Future[List[CounterpartyLimit]] - def getByCounterpartyLimitId(counterpartyLimitId: String): Future[Box[CounterpartyLimit]] - def deleteByCounterpartyLimitId(counterpartyLimitId: String): Future[Box[Boolean]] + def getCounterpartyLimit( + bankId: String, + accountId: String, + viewId: String, + counterpartyId: String + ): Future[Box[CounterpartyLimitTrait]] + + def deleteCounterpartyLimit( + bankId: String, + accountId: String, + viewId: String, + counterpartyId: String + ): Future[Box[Boolean]] + def createOrUpdateCounterpartyLimit( - counterpartyLimitId:Option[String], bankId: String, accountId: String, viewId: String, @@ -27,10 +38,10 @@ trait CounterpartyLimitProviderTrait { maxMonthlyAmount: Int, maxNumberOfMonthlyTransactions: Int, maxYearlyAmount: Int, - maxNumberOfYearlyTransactions: Int): Future[Box[CounterpartyLimit]] + maxNumberOfYearlyTransactions: Int): Future[Box[CounterpartyLimitTrait]] } -trait CounterpartyLimitTrait { +trait CounterpartyLimitTrait extends JsonAble{ def counterpartyLimitId: String def bankId: String def accountId: String diff --git a/obp-api/src/main/scala/code/counterpartylimit/MappedCounterpartyLimit.scala b/obp-api/src/main/scala/code/counterpartylimit/MappedCounterpartyLimit.scala index 7e8fb0bf3..a6bc984cb 100644 --- a/obp-api/src/main/scala/code/counterpartylimit/MappedCounterpartyLimit.scala +++ b/obp-api/src/main/scala/code/counterpartylimit/MappedCounterpartyLimit.scala @@ -1,29 +1,47 @@ package code.counterpartylimit -import code.util.{MappedUUID} +import code.util.MappedUUID import net.liftweb.common.{Box, Full} import net.liftweb.mapper._ import net.liftweb.util.Helpers.tryo - import com.openbankproject.commons.ExecutionContext.Implicits.global +import net.liftweb.json +import net.liftweb.json.Formats +import net.liftweb.json.JsonAST.{JValue,JString} +import net.liftweb.json.JsonDSL._ import scala.concurrent.Future object MappedCounterpartyLimitProvider extends CounterpartyLimitProviderTrait { - def getAll(): Future[List[CounterpartyLimit]] = Future(CounterpartyLimit.findAll()) - def getByCounterpartyLimitId(counterpartyLimitId: String): Future[Box[CounterpartyLimit]] = Future { + def getCounterpartyLimit( + bankId: String, + accountId: String, + viewId: String, + counterpartyId: String + ): Future[Box[CounterpartyLimitTrait]] = Future { CounterpartyLimit.find( - By(CounterpartyLimit.CounterpartyLimitId, counterpartyLimitId), + By(CounterpartyLimit.BankId, bankId), + By(CounterpartyLimit.AccountId, accountId), + By(CounterpartyLimit.ViewId, viewId), + By(CounterpartyLimit.CounterpartyId, counterpartyId) ) } - def deleteByCounterpartyLimitId(counterpartyLimitId: String): Future[Box[Boolean]] = Future { + + def deleteCounterpartyLimit( + bankId: String, + accountId: String, + viewId: String, + counterpartyId: String + ): Future[Box[Boolean]] = Future { CounterpartyLimit.find( - By(CounterpartyLimit.CounterpartyLimitId, counterpartyLimitId), + By(CounterpartyLimit.BankId, bankId), + By(CounterpartyLimit.AccountId, accountId), + By(CounterpartyLimit.ViewId, viewId), + By(CounterpartyLimit.CounterpartyId, counterpartyId) ).map(_.delete_!) } def createOrUpdateCounterpartyLimit( - counterpartyLimitId:Option[String], bankId: String, accountId: String, viewId: String, @@ -32,9 +50,9 @@ object MappedCounterpartyLimitProvider extends CounterpartyLimitProviderTrait { maxMonthlyAmount: Int, maxNumberOfMonthlyTransactions: Int, maxYearlyAmount: Int, - maxNumberOfYearlyTransactions: Int): Future[Box[CounterpartyLimit]]= Future { + maxNumberOfYearlyTransactions: Int)= Future { - def createCounterpartyLimit(counterpartyLimit: CounterpartyLimit): Box[CounterpartyLimit] = { + def createCounterpartyLimit(counterpartyLimit: CounterpartyLimit)= { tryo { counterpartyLimit.BankId(bankId) counterpartyLimit.AccountId(accountId) @@ -50,11 +68,14 @@ object MappedCounterpartyLimitProvider extends CounterpartyLimitProviderTrait { } def getCounterpartyLimit = CounterpartyLimit.find( - By(CounterpartyLimit.CounterpartyLimitId, counterpartyLimitId.get) + By(CounterpartyLimit.BankId, bankId), + By(CounterpartyLimit.AccountId, accountId), + By(CounterpartyLimit.ViewId, viewId), + By(CounterpartyLimit.CounterpartyId, counterpartyId), ) - val result = counterpartyLimitId match { - case Some(_) => getCounterpartyLimit.map(createCounterpartyLimit).flatten + val result = getCounterpartyLimit match { + case Full(counterpartyLimit) => createCounterpartyLimit(counterpartyLimit) case _ => createCounterpartyLimit(CounterpartyLimit.create) } result @@ -109,6 +130,17 @@ class CounterpartyLimit extends CounterpartyLimitTrait with LongKeyedMapper[Coun def maxYearlyAmount: Int = MaxYearlyAmount.get def maxNumberOfYearlyTransactions: Int = MaxNumberOfYearlyTransactions.get + override def toJValue(implicit format: Formats): JValue ={ + ("counterparty_limit_id", counterpartyLimitId) ~ + ("bank_id", bankId) ~ + ("account_id",accountId) ~ + ("view_id",viewId) ~ + ("max_single_amount", maxSingleAmount) ~ + ("max_monthly_amount", maxMonthlyAmount) ~ + ("max_number_of_monthly_transactions", maxNumberOfMonthlyTransactions) ~ + ("max_yearly_amount", maxYearlyAmount) ~ + ("max_number_of_yearly_transactions", maxNumberOfYearlyTransactions) + } } object CounterpartyLimit extends CounterpartyLimit with LongKeyedMetaMapper[CounterpartyLimit] { diff --git a/obp-api/src/test/scala/code/api/v5_1_0/CounterpartyLimitTest.scala b/obp-api/src/test/scala/code/api/v5_1_0/CounterpartyLimitTest.scala new file mode 100644 index 000000000..ace009d4d --- /dev/null +++ b/obp-api/src/test/scala/code/api/v5_1_0/CounterpartyLimitTest.scala @@ -0,0 +1,46 @@ +package code.api.v5_1_0 +import java.util.UUID +import code.api.Constant.{SYSTEM_MANAGE_CUSTOM_VIEWS_VIEW_ID, SYSTEM_OWNER_VIEW_ID} +import code.api.util.ErrorMessages._ +import code.api.v5_1_0.OBPAPI5_1_0.Implementations5_1_0 +import com.github.dwickern.macros.NameOf.nameOf +import com.openbankproject.commons.model.ErrorMessage +import com.openbankproject.commons.util.ApiVersion +import net.liftweb.json.Serialization.write +import org.scalatest.Tag + +class CounterpartyLimitTest extends V510ServerSetup { + /** + * Test tags + * Example: To run tests with tag "getPermissions": + * mvn test -D tagsToInclude + * + * This is made possible by the scalatest maven plugin + */ + object VersionOfApi extends Tag(ApiVersion.v5_1_0.toString) + object ApiEndpoint1 extends Tag(nameOf(Implementations5_1_0.createCounterpartyLimit)) + object ApiEndpoint2 extends Tag(nameOf(Implementations5_1_0.getCounterpartyLimit)) + object ApiEndpoint3 extends Tag(nameOf(Implementations5_1_0.updateCounterpartyLimit)) + object GetAccountAccessByUserId extends Tag(nameOf(Implementations5_1_0.deleteCounterpartyLimit)) + + + lazy val bankId = randomBankId + lazy val bankAccount = randomPrivateAccountViaEndpoint(bankId) + lazy val ownerView = SYSTEM_OWNER_VIEW_ID + lazy val managerCustomView = SYSTEM_MANAGE_CUSTOM_VIEWS_VIEW_ID + lazy val postCounterpartyLimitV510 = code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON.postCounterpartyLimitV510 + lazy val counterparty = createCounterparty(bankId, bankAccount.id, bankAccount.id, true, UUID.randomUUID.toString); + + + feature(s"test $ApiEndpoint1 Authorized access") { + + scenario("We will call the endpoint without user credentials", ApiEndpoint1, VersionOfApi) { + When("We make a request v5.1.0") + val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / bankAccount.id /"views" / ownerView /"counterparties" / counterparty.counterpartyId).POST + val response510 = makePostRequest(request510, write(postCounterpartyLimitV510)) + Then("We should get a 401") + response510.code should equal(401) + response510.body.extract[ErrorMessage].message should equal(UserNotLoggedIn) + } + } +} From eeb05c2a22d876d9673a2d1c7c94bc35c98835c7 Mon Sep 17 00:00:00 2001 From: hongwei Date: Tue, 25 Jun 2024 15:21:25 +0200 Subject: [PATCH 65/75] feature/V5.1.0 added CounterpartyLimit endpoint -step4 --- .../SwaggerDefinitionsJSON.scala | 10 +- .../MappedCounterpartyLimit.scala | 1 + .../api/v5_1_0/CounterpartyLimitTest.scala | 157 +++++++++++++++++- 3 files changed, 154 insertions(+), 14 deletions(-) diff --git a/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala b/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala index fab74a589..4a8d56e00 100644 --- a/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala +++ b/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala @@ -5397,11 +5397,11 @@ object SwaggerDefinitionsJSON { ) val postCounterpartyLimitV510 = PostCounterpartyLimitV510( - max_single_amount = 0, - max_monthly_amount = 0, - max_number_of_monthly_transactions = 0, - max_yearly_amount = 0, - max_number_of_yearly_transactions = 0 + max_single_amount = 200, + max_monthly_amount = 1000, + max_number_of_monthly_transactions = 10, + max_yearly_amount = 100, + max_number_of_yearly_transactions = 200 ) val counterpartyLimitV510 = CounterpartyLimitV510( diff --git a/obp-api/src/main/scala/code/counterpartylimit/MappedCounterpartyLimit.scala b/obp-api/src/main/scala/code/counterpartylimit/MappedCounterpartyLimit.scala index a6bc984cb..a919f3c96 100644 --- a/obp-api/src/main/scala/code/counterpartylimit/MappedCounterpartyLimit.scala +++ b/obp-api/src/main/scala/code/counterpartylimit/MappedCounterpartyLimit.scala @@ -135,6 +135,7 @@ class CounterpartyLimit extends CounterpartyLimitTrait with LongKeyedMapper[Coun ("bank_id", bankId) ~ ("account_id",accountId) ~ ("view_id",viewId) ~ + ("counterparty_id",counterpartyId) ~ ("max_single_amount", maxSingleAmount) ~ ("max_monthly_amount", maxMonthlyAmount) ~ ("max_number_of_monthly_transactions", maxNumberOfMonthlyTransactions) ~ diff --git a/obp-api/src/test/scala/code/api/v5_1_0/CounterpartyLimitTest.scala b/obp-api/src/test/scala/code/api/v5_1_0/CounterpartyLimitTest.scala index ace009d4d..7c7c5adf1 100644 --- a/obp-api/src/test/scala/code/api/v5_1_0/CounterpartyLimitTest.scala +++ b/obp-api/src/test/scala/code/api/v5_1_0/CounterpartyLimitTest.scala @@ -2,6 +2,7 @@ package code.api.v5_1_0 import java.util.UUID import code.api.Constant.{SYSTEM_MANAGE_CUSTOM_VIEWS_VIEW_ID, SYSTEM_OWNER_VIEW_ID} import code.api.util.ErrorMessages._ +import code.api.util.APIUtil.OAuth._ import code.api.v5_1_0.OBPAPI5_1_0.Implementations5_1_0 import com.github.dwickern.macros.NameOf.nameOf import com.openbankproject.commons.model.ErrorMessage @@ -21,26 +22,164 @@ class CounterpartyLimitTest extends V510ServerSetup { object ApiEndpoint1 extends Tag(nameOf(Implementations5_1_0.createCounterpartyLimit)) object ApiEndpoint2 extends Tag(nameOf(Implementations5_1_0.getCounterpartyLimit)) object ApiEndpoint3 extends Tag(nameOf(Implementations5_1_0.updateCounterpartyLimit)) - object GetAccountAccessByUserId extends Tag(nameOf(Implementations5_1_0.deleteCounterpartyLimit)) + object ApiEndpoint4 extends Tag(nameOf(Implementations5_1_0.deleteCounterpartyLimit)) - lazy val bankId = randomBankId - lazy val bankAccount = randomPrivateAccountViaEndpoint(bankId) - lazy val ownerView = SYSTEM_OWNER_VIEW_ID - lazy val managerCustomView = SYSTEM_MANAGE_CUSTOM_VIEWS_VIEW_ID - lazy val postCounterpartyLimitV510 = code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON.postCounterpartyLimitV510 - lazy val counterparty = createCounterparty(bankId, bankAccount.id, bankAccount.id, true, UUID.randomUUID.toString); + val bankId = testBankId1.value + val accountId = testAccountId1.value + val ownerView = SYSTEM_OWNER_VIEW_ID + val postCounterpartyLimitV510 = code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON.postCounterpartyLimitV510 + val putCounterpartyLimitV510 = PostCounterpartyLimitV510( + max_single_amount = 1, + max_monthly_amount = 2, + max_number_of_monthly_transactions = 3, + max_yearly_amount = 4, + max_number_of_yearly_transactions = 5 + ) feature(s"test $ApiEndpoint1 Authorized access") { - scenario("We will call the endpoint without user credentials", ApiEndpoint1, VersionOfApi) { + scenario("We will call the endpoint without user credentials", ApiEndpoint1, ApiEndpoint2,ApiEndpoint3,ApiEndpoint4,VersionOfApi) { + val counterparty = createCounterparty(bankId, accountId, accountId, true, UUID.randomUUID.toString); + When("We make a request v5.1.0") - val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / bankAccount.id /"views" / ownerView /"counterparties" / counterparty.counterpartyId).POST + val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId / "views" / ownerView /"counterparties" / counterparty.counterpartyId /"limits").POST val response510 = makePostRequest(request510, write(postCounterpartyLimitV510)) Then("We should get a 401") response510.code should equal(401) response510.body.extract[ErrorMessage].message should equal(UserNotLoggedIn) + + { + + val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId /"views" / ownerView /"counterparties" / counterparty.counterpartyId /"limits").PUT + val response510 = makePutRequest(request510, write(postCounterpartyLimitV510)) + Then("We should get a 401") + response510.code should equal(401) + response510.body.extract[ErrorMessage].message should equal(UserNotLoggedIn) + + } + { + val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId /"views" / ownerView /"counterparties" / counterparty.counterpartyId /"limits").GET + val response510 = makeGetRequest(request510) + Then("We should get a 401") + response510.code should equal(401) + response510.body.extract[ErrorMessage].message should equal(UserNotLoggedIn) + + } + { + val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId /"views" / ownerView /"counterparties" / counterparty.counterpartyId /"limits").DELETE + val response510 = makeDeleteRequest(request510) + Then("We should get a 401") + response510.code should equal(401) + response510.body.extract[ErrorMessage].message should equal(UserNotLoggedIn) + + } + } + + scenario("We will call the endpoint success case", ApiEndpoint1, ApiEndpoint2,ApiEndpoint3,ApiEndpoint4,VersionOfApi) { + val counterparty = createCounterparty(bankId, accountId, accountId, true, UUID.randomUUID.toString); + + When("We make a request v5.1.0") + val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId / "views" / ownerView /"counterparties" / counterparty.counterpartyId /"limits").POST <@ (user1) + val response510 = makePostRequest(request510, write(postCounterpartyLimitV510)) + Then("We should get a 201") + response510.code should equal(201) + response510.body.extract[CounterpartyLimitV510].max_monthly_amount should equal(postCounterpartyLimitV510.max_monthly_amount) + response510.body.extract[CounterpartyLimitV510].max_number_of_monthly_transactions should equal(postCounterpartyLimitV510.max_number_of_monthly_transactions) + response510.body.extract[CounterpartyLimitV510].max_number_of_yearly_transactions should equal(postCounterpartyLimitV510.max_number_of_yearly_transactions) + response510.body.extract[CounterpartyLimitV510].max_single_amount should equal(postCounterpartyLimitV510.max_single_amount) + response510.body.extract[CounterpartyLimitV510].max_yearly_amount should equal(postCounterpartyLimitV510.max_yearly_amount) + + { + val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId /"views" / ownerView /"counterparties" / counterparty.counterpartyId /"limits").GET<@ (user1) + val response510 = makeGetRequest(request510) + Then("We should get a 200") + response510.code should equal(200) + response510.body.extract[CounterpartyLimitV510].max_monthly_amount should equal(postCounterpartyLimitV510.max_monthly_amount) + response510.body.extract[CounterpartyLimitV510].max_number_of_monthly_transactions should equal(postCounterpartyLimitV510.max_number_of_monthly_transactions) + response510.body.extract[CounterpartyLimitV510].max_number_of_yearly_transactions should equal(postCounterpartyLimitV510.max_number_of_yearly_transactions) + response510.body.extract[CounterpartyLimitV510].max_single_amount should equal(postCounterpartyLimitV510.max_single_amount) + response510.body.extract[CounterpartyLimitV510].max_yearly_amount should equal(postCounterpartyLimitV510.max_yearly_amount) + + } + + { + + val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId /"views" / ownerView /"counterparties" / counterparty.counterpartyId /"limits").PUT<@ (user1) + val response510 = makePutRequest(request510, write(putCounterpartyLimitV510)) + Then("We should get a 200") + response510.code should equal(200) + response510.body.extract[CounterpartyLimitV510].max_monthly_amount should equal(putCounterpartyLimitV510.max_monthly_amount) + response510.body.extract[CounterpartyLimitV510].max_number_of_monthly_transactions should equal(putCounterpartyLimitV510.max_number_of_monthly_transactions) + response510.body.extract[CounterpartyLimitV510].max_number_of_yearly_transactions should equal(putCounterpartyLimitV510.max_number_of_yearly_transactions) + response510.body.extract[CounterpartyLimitV510].max_single_amount should equal(putCounterpartyLimitV510.max_single_amount) + response510.body.extract[CounterpartyLimitV510].max_yearly_amount should equal(putCounterpartyLimitV510.max_yearly_amount) + } + + { + val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId /"views" / ownerView /"counterparties" / counterparty.counterpartyId /"limits").GET<@ (user1) + val response510 = makeGetRequest(request510) + Then("We should get a 200") + response510.code should equal(200) + response510.body.extract[CounterpartyLimitV510].max_monthly_amount should equal(putCounterpartyLimitV510.max_monthly_amount) + response510.body.extract[CounterpartyLimitV510].max_number_of_monthly_transactions should equal(putCounterpartyLimitV510.max_number_of_monthly_transactions) + response510.body.extract[CounterpartyLimitV510].max_number_of_yearly_transactions should equal(putCounterpartyLimitV510.max_number_of_yearly_transactions) + response510.body.extract[CounterpartyLimitV510].max_single_amount should equal(putCounterpartyLimitV510.max_single_amount) + response510.body.extract[CounterpartyLimitV510].max_yearly_amount should equal(putCounterpartyLimitV510.max_yearly_amount) + } + + { + val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId /"views" / ownerView /"counterparties" / counterparty.counterpartyId /"limits").DELETE<@ (user1) + val response510 = makeDeleteRequest(request510) + Then("We should get a 204") + response510.code should equal(204) + } + } + + scenario("We will call the endpoint wrong bankId case", ApiEndpoint1, ApiEndpoint2,ApiEndpoint3,ApiEndpoint4,VersionOfApi) { + val counterparty = createCounterparty(bankId, accountId, accountId, true, UUID.randomUUID.toString); + + When("We make a request v5.1.0") + val request510 = (v5_1_0_Request / "banks" / "wrongId" / "accounts" / accountId / "views" / ownerView /"counterparties" / counterparty.counterpartyId /"limits").POST <@ (user1) + val response510 = makePostRequest(request510, write(postCounterpartyLimitV510)) + Then("We should get a 404") + response510.code should equal(404) + response510.body.extract[ErrorMessage].message contains(BankNotFound) shouldBe (true) + + { + val request510 = (v5_1_0_Request / "banks" / "wrongId" / "accounts" / accountId /"views" / ownerView /"counterparties" / counterparty.counterpartyId /"limits").GET<@ (user1) + val response510 = makeGetRequest(request510) + Then("We should get a 404") + response510.code should equal(404) + response510.body.extract[ErrorMessage].message contains(BankNotFound) shouldBe (true) + + } + + { + + val request510 = (v5_1_0_Request / "banks" / "wrongId" / "accounts" / accountId /"views" / ownerView /"counterparties" / counterparty.counterpartyId /"limits").PUT<@ (user1) + val response510 = makePutRequest(request510, write(putCounterpartyLimitV510)) + Then("We should get a 404") + response510.code should equal(404) + response510.body.extract[ErrorMessage].message contains(BankNotFound) shouldBe (true) + } + + { + val request510 = (v5_1_0_Request / "banks" / "wrongId" / "accounts" / accountId /"views" / ownerView /"counterparties" / counterparty.counterpartyId /"limits").GET<@ (user1) + val response510 = makeGetRequest(request510) + Then("We should get a 404") + response510.code should equal(404) + response510.body.extract[ErrorMessage].message contains(BankNotFound) shouldBe (true) + } + + { + val request510 = (v5_1_0_Request / "banks" / "wrongId" / "accounts" / accountId /"views" / ownerView /"counterparties" / counterparty.counterpartyId /"limits").DELETE<@ (user1) + val response510 = makeDeleteRequest(request510) + Then("We should get a 404") + response510.code should equal(404) + response510.body.extract[ErrorMessage].message contains(BankNotFound) shouldBe (true) + } } } } From dde95589d9ec9908b1dbc79055327d906d88edbb Mon Sep 17 00:00:00 2001 From: hongwei Date: Tue, 25 Jun 2024 16:37:57 +0200 Subject: [PATCH 66/75] feature/V5.1.0 added CounterpartyLimit endpoint -step5 --- .../SwaggerDefinitionsJSON.scala | 30 +++++++++---------- .../main/scala/code/api/util/APIUtil.scala | 2 +- .../scala/code/api/util/ErrorMessages.scala | 5 ++++ .../scala/code/api/util/ExampleValue.scala | 18 +++++++++++ .../scala/code/api/v5_1_0/APIMethods510.scala | 19 +++++++----- 5 files changed, 50 insertions(+), 24 deletions(-) diff --git a/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala b/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala index 4a8d56e00..28b897a38 100644 --- a/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala +++ b/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala @@ -5397,24 +5397,24 @@ object SwaggerDefinitionsJSON { ) val postCounterpartyLimitV510 = PostCounterpartyLimitV510( - max_single_amount = 200, - max_monthly_amount = 1000, - max_number_of_monthly_transactions = 10, - max_yearly_amount = 100, - max_number_of_yearly_transactions = 200 + max_single_amount = maxSingleAmountExample.value.toInt, + max_monthly_amount = maxMonthlyAmountExample.value.toInt, + max_number_of_monthly_transactions = maxNumberOfMonthlyTransactionsExample.value.toInt, + max_yearly_amount = maxYearlyAmountExample.value.toInt, + max_number_of_yearly_transactions = maxNumberOfYearlyTransactionsExample.value.toInt ) val counterpartyLimitV510 = CounterpartyLimitV510( - counterparty_limit_id = "", - bank_id = "", - account_id = "", - view_id = "", - counterparty_id = "", - max_single_amount = 0, - max_monthly_amount = 0, - max_number_of_monthly_transactions = 0, - max_yearly_amount = 0, - max_number_of_yearly_transactions = 0 + counterparty_limit_id = counterpartyLimitIdExample.value, + bank_id = bankIdExample.value, + account_id = accountIdExample.value, + view_id = viewIdExample.value, + counterparty_id = counterpartyIdExample.value, + max_single_amount = maxSingleAmountExample.value.toInt, + max_monthly_amount = maxMonthlyAmountExample.value.toInt, + max_number_of_monthly_transactions = maxNumberOfMonthlyTransactionsExample.value.toInt, + max_yearly_amount = maxYearlyAmountExample.value.toInt, + max_number_of_yearly_transactions = maxNumberOfYearlyTransactionsExample.value.toInt ) val atmsJsonV510 = AtmsJsonV510( 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 8fd6ce2f1..9c27240ef 100644 --- a/obp-api/src/main/scala/code/api/util/APIUtil.scala +++ b/obp-api/src/main/scala/code/api/util/APIUtil.scala @@ -1696,7 +1696,7 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{ private val isNeedCheckView = errorResponseBodies.contains($UserNoPermissionAccessView) && requestUrlPartPath.contains("BANK_ID") && requestUrlPartPath.contains("ACCOUNT_ID") && requestUrlPartPath.contains("VIEW_ID") - private val isNeedCheckCounterparty = errorResponseBodies.contains(CounterpartyNotFoundByCounterpartyId) && requestUrlPartPath.contains("COUNTERPARTY_ID") + private val isNeedCheckCounterparty = errorResponseBodies.contains($CounterpartyNotFoundByCounterpartyId) && requestUrlPartPath.contains("COUNTERPARTY_ID") private val reversedRequestUrl = requestUrlPartPath.reverse def getPathParams(url: List[String]): Map[String, String] = diff --git a/obp-api/src/main/scala/code/api/util/ErrorMessages.scala b/obp-api/src/main/scala/code/api/util/ErrorMessages.scala index d155f7337..06bd801cd 100644 --- a/obp-api/src/main/scala/code/api/util/ErrorMessages.scala +++ b/obp-api/src/main/scala/code/api/util/ErrorMessages.scala @@ -853,6 +853,11 @@ object ErrorMessages { * validate method: NewStyle.function.checkViewAccessAndReturnView */ def $UserNoPermissionAccessView = UserNoPermissionAccessView + + /** + * validate method: NewStyle.function.getCounterpartyByCounterpartyId + */ + def $CounterpartyNotFoundByCounterpartyId = CounterpartyNotFoundByCounterpartyId def getDuplicatedMessageNumbers = { 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 2ce079184..d180ff358 100644 --- a/obp-api/src/main/scala/code/api/util/ExampleValue.scala +++ b/obp-api/src/main/scala/code/api/util/ExampleValue.scala @@ -2147,6 +2147,24 @@ object ExampleValue { lazy val transactionRequestTypesExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) glossaryItems += makeGlossaryItem("transaction_request_types", transactionRequestTypesExample) + lazy val counterpartyLimitIdExample = ConnectorField("abc9a7e4-6d02-40e3-a129-0b2bf89de9b1","A string that MUST uniquely identify the Counterparty Limit on this OBP instance.") + glossaryItems += makeGlossaryItem("counterparty_limit_id", counterpartyLimitIdExample) + + lazy val maxSingleAmountExample = ConnectorField("1000",NoDescriptionProvided) + glossaryItems += makeGlossaryItem("max_single_amount", maxSingleAmountExample) + + lazy val maxMonthlyAmountExample = ConnectorField("10000",NoDescriptionProvided) + glossaryItems += makeGlossaryItem("max_monthly_amount", maxMonthlyAmountExample) + + lazy val maxNumberOfMonthlyTransactionsExample = ConnectorField("10",NoDescriptionProvided) + glossaryItems += makeGlossaryItem("max_number_of_monthly_transactions", maxNumberOfMonthlyTransactionsExample) + + lazy val maxYearlyAmountExample = ConnectorField("12000",NoDescriptionProvided) + glossaryItems += makeGlossaryItem("max_yearly_amount", maxYearlyAmountExample) + + lazy val maxNumberOfYearlyTransactionsExample = ConnectorField("100",NoDescriptionProvided) + glossaryItems += makeGlossaryItem("max_number_of_yearly_transactions", maxNumberOfYearlyTransactionsExample) + lazy val canAddImageUrlExample = ConnectorField(booleanTrue,NoDescriptionProvided) glossaryItems += makeGlossaryItem("can_add_image_url", canAddImageUrlExample) diff --git a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala index b62b4d06c..aa7ffcc8b 100644 --- a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala +++ b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala @@ -1,6 +1,7 @@ package code.api.v5_1_0 +import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON import code.api.{Constant, UserNotFound} import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON._ import code.api.util.APIUtil._ @@ -15,7 +16,9 @@ import code.api.util._ import code.api.util.newstyle.BalanceNewStyle import code.api.util.newstyle.Consumer.createConsumerNewStyle import code.api.util.newstyle.RegulatedEntityNewStyle.{createRegulatedEntityNewStyle, deleteRegulatedEntityNewStyle, getRegulatedEntitiesNewStyle, getRegulatedEntityByEntityIdNewStyle} +import code.api.v1_2_1.CreateViewJsonV121 import code.api.v2_1_0.{ConsumerRedirectUrlJSON, JSONFactory210} +import code.api.v2_2_0.JSONFactory220 import code.api.v3_0_0.JSONFactory300 import code.api.v3_0_0.JSONFactory300.createAggregateMetricJson import code.api.v3_1_0.ConsentJsonV310 @@ -28,14 +31,14 @@ import code.bankconnectors.Connector import code.consent.Consents import code.loginattempts.LoginAttempt import code.metrics.APIMetrics -import code.model.AppType +import code.model.{AppType, BankAccountX} import code.model.dataAccess.MappedBankAccount import code.regulatedentities.MappedRegulatedEntityProvider import code.transactionrequests.TransactionRequests.TransactionRequestTypes.{apply => _} import code.userlocks.UserLocksProvider import code.users.Users import code.util.Helper -import code.util.Helper.ObpS +import code.util.Helper.{ObpS, booleanToBox} import code.views.Views import code.views.system.{AccountAccess, ViewDefinition} import com.github.dwickern.macros.NameOf.nameOf @@ -45,9 +48,9 @@ import com.openbankproject.commons.model._ import com.openbankproject.commons.util.{ApiVersion, ScannedApiVersion} import net.liftweb.common.Full import net.liftweb.http.rest.RestHelper -import net.liftweb.json.{compactRender, parse, prettyRender} +import net.liftweb.json.{Extraction, compactRender, parse, prettyRender} import net.liftweb.mapper.By -import net.liftweb.util.Helpers +import net.liftweb.util.{Helpers, StringHelpers} import net.liftweb.util.Helpers.tryo import scala.collection.immutable.{List, Nil} @@ -2302,7 +2305,7 @@ trait APIMethods510 { $BankNotFound, $BankAccountNotFound, $UserNoPermissionAccessView, - CounterpartyNotFoundByCounterpartyId, + $CounterpartyNotFoundByCounterpartyId, InvalidJsonFormat, UnknownError ), @@ -2360,7 +2363,7 @@ trait APIMethods510 { $BankNotFound, $BankAccountNotFound, $UserNoPermissionAccessView, - CounterpartyNotFoundByCounterpartyId, + $CounterpartyNotFoundByCounterpartyId, InvalidJsonFormat, UnknownError ), @@ -2406,7 +2409,7 @@ trait APIMethods510 { $BankNotFound, $BankAccountNotFound, $UserNoPermissionAccessView, - CounterpartyNotFoundByCounterpartyId, + $CounterpartyNotFoundByCounterpartyId, InvalidJsonFormat, UnknownError ), @@ -2444,7 +2447,7 @@ trait APIMethods510 { $BankNotFound, $BankAccountNotFound, $UserNoPermissionAccessView, - CounterpartyNotFoundByCounterpartyId, + $CounterpartyNotFoundByCounterpartyId, InvalidJsonFormat, UnknownError ), From 88108c39b46a351a45a706bf138e101a213f2875 Mon Sep 17 00:00:00 2001 From: hongwei Date: Thu, 27 Jun 2024 13:22:13 +0200 Subject: [PATCH 67/75] feature/OBPv510 new version create view endpoint- step2 --- .../SwaggerDefinitionsJSON.scala | 9 + .../main/scala/code/api/util/APIUtil.scala | 15 +- .../scala/code/api/util/ErrorMessages.scala | 5 +- .../scala/code/api/v5_1_0/APIMethods510.scala | 234 ++++++++++++++++++ .../code/api/v5_1_0/JSONFactory5.1.0.scala | 78 +++++- .../main/scala/code/views/MapperViews.scala | 4 +- .../scala/code/util/APIUtilHeavyTest.scala | 27 ++ .../commons/model/ViewModel.scala | 4 +- 8 files changed, 367 insertions(+), 9 deletions(-) diff --git a/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala b/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala index 28b897a38..9f945858d 100644 --- a/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala +++ b/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala @@ -314,6 +314,15 @@ object SwaggerDefinitionsJSON { "can_revoke_access_to_custom_views" ) +// val createCustomViewJson = CustomViewJsonV510( +// name = "_test", +// description= "This view is for family", +// metadata_view= "This view is for family", +// is_public = true, +// which_alias_to_use ="family", +// hide_metadata_if_alias_used = true, +// allowed_permissions= allowedActionsV500, +// ) val createSystemViewJsonV500 = CreateViewJsonV500( name = "_test", description = "This view is for family", 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 9c27240ef..74f57dfa9 100644 --- a/obp-api/src/main/scala/code/api/util/APIUtil.scala +++ b/obp-api/src/main/scala/code/api/util/APIUtil.scala @@ -122,7 +122,7 @@ import code.api.v2_1_0.OBPAPI2_1_0.Implementations2_1_0 import code.api.v2_2_0.OBPAPI2_2_0.Implementations2_2_0 import code.etag.MappedETag import code.users.Users -import code.views.system.AccountAccess +import code.views.system.{AccountAccess, ViewDefinition} import net.liftweb.mapper.By import scala.collection.mutable @@ -4962,4 +4962,17 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{ .map(item => BankIdAccountId(BankId(item.bank_id.get), AccountId(item.account_id.get))) .distinct // List pairs (bank_id, account_id) } + + //get all the permission Pair from one record, eg: + //List("can_see_transaction_this_bank_account","can_see_transaction_requests"....) + //Note, do not contain can_revoke_access_to_views and can_grant_access_to_views permission yet. + def getViewPermissions(view: ViewDefinition) = view.allFields.map(x => (x.name, x.get)) + .filter(pair =>pair._2.isInstanceOf[Boolean]) + .filter(pair => pair._1.startsWith("can")) + .filter(pair => pair._2.equals(true)) + .map(pair => + StringHelpers.snakify(pair._1) + .dropRight(1) //Remove the "_" in the end, eg canCreateStandingOrder_ --> canCreateStandingOrder + ).toSet + } \ No newline at end of file diff --git a/obp-api/src/main/scala/code/api/util/ErrorMessages.scala b/obp-api/src/main/scala/code/api/util/ErrorMessages.scala index 06bd801cd..985c34f07 100644 --- a/obp-api/src/main/scala/code/api/util/ErrorMessages.scala +++ b/obp-api/src/main/scala/code/api/util/ErrorMessages.scala @@ -198,6 +198,8 @@ object ErrorMessages { s"OBP-20048: If target viewId is system view, the current view.can_revoke_access_to_views does not contains it. Or" + s"if target viewId is custom view, the current view.can_revoke_access_to_custom_views is false." + val SourceViewHasLessPermission = "OBP-20050: Source view contains less permissions than target view." + val UserNotSuperAdmin = "OBP-20050: Current User is not a Super Admin!" val ElasticSearchIndexNotFound = "OBP-20051: Elasticsearch index or indices not found." @@ -499,7 +501,7 @@ object ErrorMessages { val DeleteSystemViewError = "OBP-30251: Could not delete the system view" val SystemViewNotFound = "OBP-30252: System view not found. Please specify a valid value for VIEW_ID" val UpdateSystemViewError = "OBP-30253: Could not update the system view" - val ExistingSystemViewError = "OBP-30254: There is already a view with permalink" + val ExistingSystemViewError = "OBP-30254: The system view is already Existing." val EmptyNameOfSystemViewError = "OBP-30255: You cannot create a View with an empty Name" val DeleteCustomViewError = "OBP-30256: Could not delete the custom view" val CannotFindCustomViewError = "OBP-30257: Could not find the custom view" @@ -511,6 +513,7 @@ object ErrorMessages { val GetCounterpartyLimitError = "OBP-30263: Counterparty limit not found. Please specify a valid value for BANK_ID, ACCOUNT_ID, VIEW_ID or COUNTERPARTY_ID." val CounterpartyLimitAlreadyExists = "OBP-30264: Counterparty limit already exists. Please specify a different value for BANK_ID, ACCOUNT_ID, VIEW_ID or COUNTERPARTY_ID." val DeleteCounterpartyLimitError = "OBP-30265: Could not delete the counterparty limit." + val ExistingCustomViewError = "OBP-30266: The custom view is already Existing." val TaxResidenceNotFound = "OBP-30300: Tax Residence not found by TAX_RESIDENCE_ID. " val CustomerAddressNotFound = "OBP-30310: Customer's Address not found by CUSTOMER_ADDRESS_ID. " diff --git a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala index aa7ffcc8b..73c0eaaf1 100644 --- a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala +++ b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala @@ -2469,6 +2469,240 @@ trait APIMethods510 { } } } + + resourceDocs += ResourceDoc( + createCustomView, + implementedInApiVersion, + nameOf(createCustomView), + "POST", + "/banks/BANK_ID/accounts/ACCOUNT_ID/views/VIEW_ID/target-views", + "Create Custom View", + s"""Create a custom view on bank account + | + | ${authenticationRequiredMessage(true)} and the user needs to have access to the owner view. + | The 'alias' field in the JSON can take one of three values: + | + | * _public_: to use the public alias if there is one specified for the other account. + | * _private_: to use the private alias if there is one specified for the other account. + | + | * _''(empty string)_: to use no alias; the view shows the real name of the other account. + | + | The 'hide_metadata_if_alias_used' field in the JSON can take boolean values. If it is set to `true` and there is an alias on the other account then the other accounts' metadata (like more_info, url, image_url, open_corporates_url, etc.) will be hidden. Otherwise the metadata will be shown. + | + | The 'allowed_actions' field is a list containing the name of the actions allowed on this view, all the actions contained will be set to `true` on the view creation, the rest will be set to `false`. + | + | You MUST use a leading _ (underscore) in the view name because other view names are reserved for OBP [system views](/index#group-View-System). + | """, + SwaggerDefinitionsJSON.createViewJsonV300, + viewJsonV300, + List( + $UserNotLoggedIn, + $BankNotFound, + $BankAccountNotFound, + $UserNoPermissionAccessView, + InvalidJsonFormat, + UnknownError + ), + List(apiTagView, apiTagAccount) + ) + lazy val createCustomView: OBPEndpoint = { + case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: "views" :: ViewId(viewId) ::"target-views" :: Nil JsonPost json -> _ => { + cc => + implicit val ec = EndpointContext(Some(cc)) + for { + (_, _, _, view, callContext) <- SS.userBankAccountView + createCustomViewJson <- NewStyle.function.tryons(s"$InvalidJsonFormat The Json body should be the ${classOf[CreateViewJson]}", 400, cc.callContext) { + json.extract[CreateCustomViewJson] + } + //customer views are started ith `_`,eg _life, _work, and System views startWith letter, eg: owner + _ <- Helper.booleanToFuture(failMsg = InvalidCustomViewFormat + s"Current view_name (${createCustomViewJson.name})", cc = callContext) { + isValidCustomViewName(createCustomViewJson.name) + } + + permissionsFromSource = APIUtil.getViewPermissions(view.asInstanceOf[ViewDefinition]) + permissionsFromTarget = createCustomViewJson.allowed_permissions + + _ <- Helper.booleanToFuture(failMsg = SourceViewHasLessPermission + s"Current source view permissions ($permissionsFromSource), target view permissions ($permissionsFromTarget)", cc = callContext) { + permissionsFromTarget.toSet.subsetOf(permissionsFromSource) + } + + failMsg = s"${ErrorMessages.ViewDoesNotPermitAccess} You need the `${StringHelpers.snakify(nameOf(view.canCreateCustomView))}` permission on VIEW_ID(${viewId.value})" + + _ <- Helper.booleanToFuture(failMsg, cc = callContext) { + view.canCreateCustomView + } + (view, callContext) <- NewStyle.function.createCustomView(BankIdAccountId(bankId, accountId), createCustomViewJson.toCreateViewJson, callContext) + } yield { + (JSONFactory510.createViewJson(view), HttpCode.`201`(callContext)) + } + } + } + + resourceDocs += ResourceDoc( + updateCustomView, + implementedInApiVersion, + nameOf(updateCustomView), + "PUT", + "/banks/BANK_ID/accounts/ACCOUNT_ID/views/VIEW_ID/target-views/TARGET_VIEW_ID", + "Update Custom View", + s"""Update an existing custom view on a bank account + | + |${authenticationRequiredMessage(true)} and the user needs to have access to the owner view. + | + |The json sent is the same as during view creation (above), with one difference: the 'name' field + |of a view is not editable (it is only set when a view is created)""", + updateViewJsonV300, + viewJsonV300, + List( + $UserNotLoggedIn, + $BankNotFound, + $BankAccountNotFound, + $UserNoPermissionAccessView, + InvalidJsonFormat, + UnknownError + ), + List(apiTagView, apiTagAccount) + ) + lazy val updateCustomView: OBPEndpoint = { + case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: "views" :: ViewId(viewId) :: "target-views" :: ViewId(targetViewId) :: Nil JsonPut json -> _ => { + cc => + implicit val ec = EndpointContext(Some(cc)) + for { + (_, _, _, view, callContext) <- SS.userBankAccountView + targetCreateCustomViewJson <- NewStyle.function.tryons(s"$InvalidJsonFormat The Json body should be the ${classOf[UpdateCustomViewJson]}", 400, cc.callContext) { + json.extract[UpdateCustomViewJson] + } + //customer views are started ith `_`,eg _life, _work, and System views startWith letter, eg: owner + _ <- Helper.booleanToFuture(failMsg = InvalidCustomViewFormat + s"Current TARGET_VIEW_ID (${targetViewId})", cc = callContext) { + isValidCustomViewId(targetViewId.value) + } + permissionsFromSource = APIUtil.getViewPermissions(view.asInstanceOf[ViewDefinition]) + permissionsFromTarget = targetCreateCustomViewJson.allowed_actions + + _ <- Helper.booleanToFuture(failMsg = SourceViewHasLessPermission + s"Current source view permissions ($permissionsFromSource), target view permissions ($permissionsFromTarget)", cc = callContext) { + permissionsFromTarget.toSet.subsetOf(permissionsFromSource) + } + + failmsg = s"${ErrorMessages.ViewDoesNotPermitAccess} You need the `${StringHelpers.snakify(nameOf(view.canCreateCustomView))}` permission on VIEW_ID(${viewId.value})" + + _ <- Helper.booleanToFuture(failmsg, cc = callContext) { + view.canCreateCustomView + } + + (view, callContext) <- NewStyle.function.updateCustomView(BankIdAccountId(bankId, accountId), targetViewId, targetCreateCustomViewJson.toUpdateViewJson, callContext) + } yield { + (JSONFactory510.createViewJson(view), HttpCode.`200`(callContext)) + } + } + } + + staticResourceDocs += ResourceDoc( + getCustomView, + implementedInApiVersion, + nameOf(getCustomView), + "GET", + "/banks/BANK_ID/accounts/ACCOUNT_ID/views/VIEW_ID/target-views/TARGET_VIEW_ID", + "Get Custom View", + s"""#Views + | + | + |Views in Open Bank Project provide a mechanism for fine grained access control and delegation to Accounts and Transactions. Account holders use the 'owner' view by default. Delegated access is made through other views for example 'accountants', 'share-holders' or 'tagging-application'. Views can be created via the API and each view has a list of entitlements. + | + |Views on accounts and transactions filter the underlying data to redact certain fields for certain users. For instance the balance on an account may be hidden from the public. The way to know what is possible on a view is determined in the following JSON. + | + |**Data:** When a view moderates a set of data, some fields my contain the value `null` rather than the original value. This indicates either that the user is not allowed to see the original data or the field is empty. + | + |There is currently one exception to this rule; the 'holder' field in the JSON contains always a value which is either an alias or the real name - indicated by the 'is_alias' field. + | + |**Action:** When a user performs an action like trying to post a comment (with POST API call), if he is not allowed, the body response will contain an error message. + | + |**Metadata:** + |Transaction metadata (like images, tags, comments, etc.) will appears *ONLY* on the view where they have been created e.g. comments posted to the public view only appear on the public view. + | + |The other account metadata fields (like image_URL, more_info, etc.) are unique through all the views. Example, if a user edits the 'more_info' field in the 'team' view, then the view 'authorities' will show the new value (if it is allowed to do it). + | + |# All + |*Optional* + | + |Returns the list of the views created for account ACCOUNT_ID at BANK_ID. + | + |${authenticationRequiredMessage(true)} and the user needs to have access to the owner view.""", + EmptyBody, + viewsJsonV500, + List( + $UserNotLoggedIn, + $BankNotFound, + $BankAccountNotFound, + $UserNoPermissionAccessView, + UnknownError + ), + List(apiTagView, apiTagAccount) + ) + lazy val getCustomView: OBPEndpoint = { + //get the available views on an bank account + case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: "views" :: ViewId(viewId) :: "target-views" :: ViewId(targetViewId):: Nil JsonGet req => { + cc => + implicit val ec = EndpointContext(Some(cc)) + for { + (_, _, _, view, callContext) <- SS.userBankAccountView + //customer views are started ith `_`,eg _life, _work, and System views startWith letter, eg: owner + _ <- Helper.booleanToFuture(failMsg = InvalidCustomViewFormat + s"Current TARGET_VIEW_ID (${targetViewId.value})", cc = callContext) { + isValidCustomViewId(targetViewId.value) + } + failmsg = s"${ErrorMessages.ViewDoesNotPermitAccess} You need the `${StringHelpers.snakify(nameOf(view.canSeeAvailableViewsForBankAccount))}`permission on any your views. Current VIEW_ID (${viewId.value})" + _ <- Helper.booleanToFuture(failmsg, cc = callContext) { + view.canSeeAvailableViewsForBankAccount + } + targetView <- NewStyle.function.customView(targetViewId, BankIdAccountId(bankId, accountId), callContext) + } yield { + (JSONFactory510.createViewJson(targetView), HttpCode.`200`(callContext)) + } + } + } + + + staticResourceDocs += ResourceDoc( + deleteCustomView, + implementedInApiVersion, + nameOf(deleteCustomView), + "DELETE", + "/banks/BANK_ID/accounts/ACCOUNT_ID/views/VIEW_ID/target-views/TARGET_VIEW_ID", + "Delete Custom View", + "Deletes the custom view specified by VIEW_ID on the bank account specified by ACCOUNT_ID at bank BANK_ID", + EmptyBody, + EmptyBody, + List( + $UserNotLoggedIn, + $BankNotFound, + $BankAccountNotFound, + $UserNoPermissionAccessView, + UnknownError + ), + List(apiTagView, apiTagAccount) + ) + + lazy val deleteCustomView: OBPEndpoint = { + //deletes a view on an bank account + case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId ) :: "views" :: ViewId(viewId) :: "target-views" :: ViewId(targetViewId) :: Nil JsonDelete req => { + cc => implicit val ec = EndpointContext(Some(cc)) + for { + (_, _, _, view, callContext) <- SS.userBankAccountView + //customer views are started ith `_`,eg _life, _work, and System views startWith letter, eg: owner + _ <- Helper.booleanToFuture(failMsg = InvalidCustomViewFormat + s"Current TARGET_VIEW_ID (${targetViewId.value})", cc = callContext) { + isValidCustomViewId(targetViewId.value) + } + failMsg = s"${ErrorMessages.ViewDoesNotPermitAccess} You need the `${StringHelpers.snakify(nameOf(view.canDeleteCustomView))}` permission on any your views.Current VIEW_ID (${viewId.value})" + _ <- Helper.booleanToFuture(failMsg, cc = callContext) { + view.canDeleteCustomView + } + _ <- NewStyle.function.customView(targetViewId, BankIdAccountId(bankId, accountId), callContext) + deleted <- NewStyle.function.removeCustomView(targetViewId, BankIdAccountId(bankId, accountId), callContext) + } yield { + (Full(deleted), HttpCode.`204`(callContext)) + } + } + } + } } diff --git a/obp-api/src/main/scala/code/api/v5_1_0/JSONFactory5.1.0.scala b/obp-api/src/main/scala/code/api/v5_1_0/JSONFactory5.1.0.scala index 14fd4c4f1..89d2ea861 100644 --- a/obp-api/src/main/scala/code/api/v5_1_0/JSONFactory5.1.0.scala +++ b/obp-api/src/main/scala/code/api/v5_1_0/JSONFactory5.1.0.scala @@ -28,17 +28,17 @@ package code.api.v5_1_0 import code.api.Constant import code.api.util.{APIUtil, ConsentJWT, CustomJsonFormats, JwtUtil, Role} -import code.api.util.APIUtil.gitCommit +import code.api.util.APIUtil.{gitCommit, stringOrNull} import code.api.v1_4_0.JSONFactory1_4_0.{LocationJsonV140, MetaJsonV140, transformToLocationFromV140, transformToMetaFromV140} import code.api.v2_1_0.ResourceUserJSON import code.api.v3_0_0.JSONFactory300.{createLocationJson, createMetaJson, transformToAddressFromV300} -import code.api.v3_0_0.{AccountIdJson, AccountsIdsJsonV300, AddressJsonV300, OpeningTimesV300} +import code.api.v3_0_0.{AccountIdJson, AccountsIdsJsonV300, AddressJsonV300, OpeningTimesV300, ViewJsonV300} import code.api.v4_0_0.{EnergySource400, HostedAt400, HostedBy400, PostViewJsonV400} import code.atmattribute.AtmAttribute import code.atms.Atms.Atm import code.users.{UserAttribute, Users} import code.views.system.{AccountAccess, ViewDefinition} -import com.openbankproject.commons.model.{Address, AtmId, AtmT, BankId, BankIdAccountId, Customer, Location, Meta, RegulatedEntityTrait} +import com.openbankproject.commons.model.{Address, AtmId, AtmT, BankId, BankIdAccountId, CreateViewJson, Customer, Location, Meta, RegulatedEntityTrait, UpdateViewJSON, View} import com.openbankproject.commons.util.{ApiVersion, ScannedApiVersion} import java.util.Date @@ -332,8 +332,80 @@ case class PostCreateUserAccountAccessJsonV510(username: String, provider:String case class PostAccountAccessJsonV510(user_id: String, view_id: String) +case class CreateCustomViewJson( + name: String, + description: String, + metadata_view: String, + is_public: Boolean, + which_alias_to_use: String, + hide_metadata_if_alias_used: Boolean, + allowed_permissions : List[String], +) { + def toCreateViewJson = CreateViewJson( + name: String, + description: String, + metadata_view: String, + is_public: Boolean, + which_alias_to_use: String, + hide_metadata_if_alias_used: Boolean, + allowed_actions = allowed_permissions, + ) +} + +case class UpdateCustomViewJson( + description: String, + metadata_view: String, + is_public: Boolean, + which_alias_to_use: String, + hide_metadata_if_alias_used: Boolean, + allowed_actions: List[String] +) { + def toUpdateViewJson = UpdateViewJSON( + description: String, + metadata_view: String, + is_public: Boolean, + is_firehose= None, + which_alias_to_use: String, + hide_metadata_if_alias_used: Boolean, + allowed_actions: List[String] + ) +} + +case class CustomViewJsonV510( + id: String, + short_name: String, + description: String, + metadata_view: String, + is_public: Boolean, + is_system: Boolean, + is_firehose: Option[Boolean] = None, + alias: String, + hide_metadata_if_alias_used: Boolean, + allowed_permissions: List[String] +) + object JSONFactory510 extends CustomJsonFormats { + def createViewJson(view: View): CustomViewJsonV510 = { + val alias = + if (view.usePublicAliasIfOneExists) + "public" + else if (view.usePrivateAliasIfOneExists) + "private" + else + "" + CustomViewJsonV510( + id = view.viewId.value, + short_name = stringOrNull(view.name), + description = stringOrNull(view.description), + metadata_view = view.metadataView, + is_public = view.isPublic, + is_system = view.isSystem, + alias = alias, + hide_metadata_if_alias_used = view.hideOtherAccountMetadataIfAlias, + allowed_permissions = APIUtil.getViewPermissions(view.asInstanceOf[ViewDefinition]).toList + ) + } def createCustomersIds(customers : List[Customer]): CustomersIdsJsonV510 = CustomersIdsJsonV510(customers.map(x => CustomerIdJson(x.customerId))) diff --git a/obp-api/src/main/scala/code/views/MapperViews.scala b/obp-api/src/main/scala/code/views/MapperViews.scala index 2119cb30e..36270c7fc 100644 --- a/obp-api/src/main/scala/code/views/MapperViews.scala +++ b/obp-api/src/main/scala/code/views/MapperViews.scala @@ -399,7 +399,7 @@ object MapperViews extends Views with MdcLoggable { existing match { case true => - Failure(s"$ExistingSystemViewError $viewId") + Failure(s"$ExistingSystemViewError Current VIEW_ID($viewId)") case false => val createdView = ViewDefinition.create.name_(view.name).view_id(viewId) createdView.setFromViewData(view) @@ -436,7 +436,7 @@ object MapperViews extends Views with MdcLoggable { ) == 1 if (existing) - Failure(s"There is already a view with permalink $viewId on this bank account") + Failure(s"$ExistingCustomViewError Current BankId(${bankAccountId.bankId.value}), AccountId(${bankAccountId.accountId.value}), ViewId($viewId).") else { val createdView = ViewDefinition.create. name_(view.name). diff --git a/obp-api/src/test/scala/code/util/APIUtilHeavyTest.scala b/obp-api/src/test/scala/code/util/APIUtilHeavyTest.scala index 798c75542..ade584eb3 100644 --- a/obp-api/src/test/scala/code/util/APIUtilHeavyTest.scala +++ b/obp-api/src/test/scala/code/util/APIUtilHeavyTest.scala @@ -27,6 +27,7 @@ TESOBE (http://www.tesobe.com/) package code.util +import code.api.Constant.SYSTEM_OWNER_VIEW_ID import code.api.UKOpenBanking.v2_0_0.{APIMethods_UKOpenBanking_200, OBP_UKOpenBanking_200} import code.api.UKOpenBanking.v3_1_0.{APIMethods_AccountAccessApi, OBP_UKOpenBanking_310} import code.api.berlin.group.v1_3.OBP_BERLIN_GROUP_1_3 @@ -37,6 +38,7 @@ import code.api.v3_1_0.OBPAPI3_1_0 import code.api.v4_0_0.OBPAPI4_0_0.Implementations4_0_0 import code.api.v4_0_0.{OBPAPI4_0_0, V400ServerSetup} import code.setup.PropsReset +import code.views.system.ViewDefinition import com.openbankproject.commons.util.ApiVersion class APIUtilHeavyTest extends V400ServerSetup with PropsReset { @@ -164,5 +166,30 @@ class APIUtilHeavyTest extends V400ServerSetup with PropsReset { allowedOperationIds5 contains("UKv3.1-createAccountAccessConsents") should be (true) allowedOperationIds5 contains("UKv3.1-deleteConsent") should be (false) } + + feature("test APIUtil.getPermissionPairFromViewDefinition method") { + + scenario(s"Test the getPermissionPairFromViewDefinition method") { + + val subList = List( + "can_see_transaction_request_types", + "can_see_available_views_for_bank_account", + "can_see_views_with_permissions_for_all_users", + "can_see_views_with_permissions_for_one_user", + "can_see_transaction_this_bank_account", + "can_see_transaction_other_bank_account", + "can_see_transaction_description", + "can_see_transaction_start_date", + "can_see_transaction_finish_date", + "can_see_bank_account_national_identifier", + "can_see_bank_account_swift_bic" + ).toSet + val systemOwnerView = getOrCreateSystemView(SYSTEM_OWNER_VIEW_ID) + val permissions = APIUtil.getViewPermissions(systemOwnerView.asInstanceOf[ViewDefinition]) + + subList.subsetOf(permissions) + } + + } } \ No newline at end of file diff --git a/obp-commons/src/main/scala/com/openbankproject/commons/model/ViewModel.scala b/obp-commons/src/main/scala/com/openbankproject/commons/model/ViewModel.scala index 45147f229..256ba39ec 100644 --- a/obp-commons/src/main/scala/com/openbankproject/commons/model/ViewModel.scala +++ b/obp-commons/src/main/scala/com/openbankproject/commons/model/ViewModel.scala @@ -57,7 +57,7 @@ trait ViewSpecification { } /* -The JSON that should be supplied to create a view. Conforms to ViewSpecification +The JSON that should be supplied to create a custom view. Conforms to ViewSpecification */ case class CreateViewJson( name: String, @@ -73,7 +73,7 @@ case class CreateViewJson( /* -The JSON that should be supplied to update a view. Conforms to ViewSpecification +The JSON that should be supplied to update a system view. Conforms to ViewSpecification */ case class UpdateViewJSON( description: String, From b558c54359ba1568a0f9234c1e531f2221b25209 Mon Sep 17 00:00:00 2001 From: Simon Redfern Date: Thu, 27 Jun 2024 16:41:43 +0200 Subject: [PATCH 68/75] tweaking create system view Resource Doc --- .../main/scala/code/api/v5_0_0/APIMethods500.scala | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala b/obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala index 7852849d1..7f55f5010 100644 --- a/obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala +++ b/obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala @@ -1861,18 +1861,19 @@ trait APIMethods500 { s"""Create a system view | | ${authenticationRequiredMessage(true)} and the user needs to have access to the $canCreateSystemView entitlement. - | The 'alias' field in the JSON can take one of two values: + | + | The 'allowed_actions' field is a list containing the names of the actions allowed through this view. + | All the actions contained in the list will be set to `true` on the view creation, the rest will be set to `false`. + | + | The 'alias' field in the JSON can take one of three values: | | * _public_: to use the public alias if there is one specified for the other account. | * _private_: to use the private alias if there is one specified for the other account. - | | * _''(empty string)_: to use no alias; the view shows the real name of the other account. | | The 'hide_metadata_if_alias_used' field in the JSON can take boolean values. If it is set to `true` and there is an alias on the other account then the other accounts' metadata (like more_info, url, image_url, open_corporates_url, etc.) will be hidden. Otherwise the metadata will be shown. | - | The 'allowed_actions' field is a list containing the name of the actions allowed on this view, all the actions contained will be set to `true` on the view creation, the rest will be set to `false`. - | - | Please note that system views cannot be public. In case you try to set it you will get the error $SystemViewCannotBePublicError + | System views cannot be public. In case you try to set it you will get the error $SystemViewCannotBePublicError | """, createSystemViewJsonV500, viewJsonV500, From 78853ee09a289b741341fc0e6c10eefe3ff8b38f Mon Sep 17 00:00:00 2001 From: hongwei Date: Thu, 27 Jun 2024 16:57:04 +0200 Subject: [PATCH 69/75] feature/OBPv510 new version create view endpoint- step3 --- .../SwaggerDefinitionsJSON.scala | 57 +++++++++++++------ .../scala/code/api/util/ExampleValue.scala | 2 +- .../scala/code/api/v5_1_0/APIMethods510.scala | 10 ++-- .../code/api/v5_1_0/JSONFactory5.1.0.scala | 7 +-- 4 files changed, 47 insertions(+), 29 deletions(-) diff --git a/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala b/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala index 9f945858d..bafd14081 100644 --- a/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala +++ b/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala @@ -36,7 +36,7 @@ import com.openbankproject.commons.util.{ApiVersion, FieldNameApiVersions, Refle import net.liftweb.json import java.net.URLEncoder -import code.api.v5_1_0.{AtmsJsonV510, _} +import code.api.v5_1_0.{AtmsJsonV510, CustomViewJsonV510, _} import code.endpointMapping.EndpointMappingCommons import net.liftweb.json.Extraction @@ -314,26 +314,47 @@ object SwaggerDefinitionsJSON { "can_revoke_access_to_custom_views" ) -// val createCustomViewJson = CustomViewJsonV510( -// name = "_test", -// description= "This view is for family", -// metadata_view= "This view is for family", -// is_public = true, -// which_alias_to_use ="family", -// hide_metadata_if_alias_used = true, -// allowed_permissions= allowedActionsV500, -// ) + val createCustomViewJson = CreateCustomViewJson( + name = viewNameExample.value, + description= viewDescriptionExample.value, + metadata_view= metadataViewExample.value, + is_public = isPublicExample.value, + which_alias_to_use = whichAliasToUseExample.value, + hide_metadata_if_alias_used = hideMetadataIfAliasUsedExample.value.toBoolean, + allowed_permissions= allowedActionsV500, + ) + + val customViewJsonV510 = CustomViewJsonV510( + id = viewIdExample.value, + name = viewNameExample.value, + description = viewDescriptionExample.value, + metadata_view = metadataViewExample.value, + is_public = isPublicExample.value, + alias = whichAliasToUseExample.value, + hide_metadata_if_alias_used = hideMetadataIfAliasUsedExample.value.toBoolean, + allowed_permissions = allowedActionsV500 + ) + val createSystemViewJsonV500 = CreateViewJsonV500( - name = "_test", - description = "This view is for family", - metadata_view ="_test", - is_public = false, - which_alias_to_use = "family", - hide_metadata_if_alias_used = false, + name = viewNameExample.value, + description = viewDescriptionExample.value, + metadata_view =viewDescriptionExample.value, + is_public = isPublicExample.value, + alias = whichAliasToUseExample.value, + hide_metadata_if_alias_used = hideMetadataIfAliasUsedExample.value.toBoolean, allowed_actions = allowedActionsV500, // Version 5.0.0 - can_grant_access_to_views = Some(List("owner")), - can_revoke_access_to_views = Some(List("owner")) + can_grant_access_to_views = Some(List(viewIdExample.value)), + can_revoke_access_to_views = Some(List(viewIdExample.value)) + ) + + val updateCustomViewJson = UpdateCustomViewJson( + description = viewDescriptionExample.value, + metadata_view = metadataViewExample.value, + is_public = isPublicExample.value, + alias = whichAliasToUseExample.value, + hide_metadata_if_alias_used = hideMetadataIfAliasUsedExample.value.toBoolean, + allowed_permissions = allowedActionsV500 ) val updateViewJsonV300 = UpdateViewJsonV300( 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 d180ff358..3293906ad 100644 --- a/obp-api/src/main/scala/code/api/util/ExampleValue.scala +++ b/obp-api/src/main/scala/code/api/util/ExampleValue.scala @@ -1171,7 +1171,7 @@ object ExampleValue { lazy val accountOtpExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) glossaryItems += makeGlossaryItem("account_otp", accountOtpExample) - lazy val hideMetadataIfAliasUsedExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) + lazy val hideMetadataIfAliasUsedExample = ConnectorField("true",NoDescriptionProvided) glossaryItems += makeGlossaryItem("hide_metadata_if_alias_used", hideMetadataIfAliasUsedExample) lazy val canSeeBankAccountCurrencyExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) diff --git a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala index 73c0eaaf1..169c7e2c1 100644 --- a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala +++ b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala @@ -2493,8 +2493,8 @@ trait APIMethods510 { | | You MUST use a leading _ (underscore) in the view name because other view names are reserved for OBP [system views](/index#group-View-System). | """, - SwaggerDefinitionsJSON.createViewJsonV300, - viewJsonV300, + createCustomViewJson, + customViewJsonV510, List( $UserNotLoggedIn, $BankNotFound, @@ -2551,8 +2551,8 @@ trait APIMethods510 { | |The json sent is the same as during view creation (above), with one difference: the 'name' field |of a view is not editable (it is only set when a view is created)""", - updateViewJsonV300, - viewJsonV300, + updateCustomViewJson, + customViewJsonV510, List( $UserNotLoggedIn, $BankNotFound, @@ -2628,7 +2628,7 @@ trait APIMethods510 { | |${authenticationRequiredMessage(true)} and the user needs to have access to the owner view.""", EmptyBody, - viewsJsonV500, + customViewJsonV510, List( $UserNotLoggedIn, $BankNotFound, diff --git a/obp-api/src/main/scala/code/api/v5_1_0/JSONFactory5.1.0.scala b/obp-api/src/main/scala/code/api/v5_1_0/JSONFactory5.1.0.scala index 89d2ea861..c1ca3d486 100644 --- a/obp-api/src/main/scala/code/api/v5_1_0/JSONFactory5.1.0.scala +++ b/obp-api/src/main/scala/code/api/v5_1_0/JSONFactory5.1.0.scala @@ -373,12 +373,10 @@ case class UpdateCustomViewJson( case class CustomViewJsonV510( id: String, - short_name: String, + name: String, description: String, metadata_view: String, is_public: Boolean, - is_system: Boolean, - is_firehose: Option[Boolean] = None, alias: String, hide_metadata_if_alias_used: Boolean, allowed_permissions: List[String] @@ -396,11 +394,10 @@ object JSONFactory510 extends CustomJsonFormats { "" CustomViewJsonV510( id = view.viewId.value, - short_name = stringOrNull(view.name), + name = stringOrNull(view.name), description = stringOrNull(view.description), metadata_view = view.metadataView, is_public = view.isPublic, - is_system = view.isSystem, alias = alias, hide_metadata_if_alias_used = view.hideOtherAccountMetadataIfAlias, allowed_permissions = APIUtil.getViewPermissions(view.asInstanceOf[ViewDefinition]).toList From d285ce7d790757903d7a8d1ffd65ea865327d35f Mon Sep 17 00:00:00 2001 From: Simon Redfern Date: Thu, 27 Jun 2024 17:01:56 +0200 Subject: [PATCH 70/75] Tweaking Create Counterparty Limit Resource Doc --- .../scala/code/api/v5_1_0/APIMethods510.scala | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala index aa7ffcc8b..4919a9b2a 100644 --- a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala +++ b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala @@ -2290,6 +2290,18 @@ trait APIMethods510 { } } + + lazy val counterPartyLimitIntro: String = + """Counter Party Limits can be used to restrict the Transaction Request amounts and frequencies (per month and year) that can be made to a Counterparty (Beneficiary). + | + |In order to implement VRP (Variable Recurring Payments) perform the following steps: + |1) Create a Custom View named e.g. VRP1. + |2) Place a Beneficiary Counterparty on that view. + |3) Add Counterparty Limits for that Counterparty. + |4) Generate a Consent containing the bank, account and view (e.g. VRP1) + |5) Let the App use the consent to trigger Transaction Requests. + |""".stripMargin + staticResourceDocs += ResourceDoc( createCounterpartyLimit, implementedInApiVersion, @@ -2297,7 +2309,11 @@ trait APIMethods510 { "POST", "/banks/BANK_ID/accounts/ACCOUNT_ID/views/VIEW_ID/counterparties/COUNTERPARTY_ID/limits", "Create Counterparty Limit", - s"""Create Counterparty Limit.""", + s"""Create Counterparty Limit. + | + |$counterPartyLimitIntro + | + |""".stripMargin, postCounterpartyLimitV510, counterpartyLimitV510, List( From a7e28ccb4dff02d2c4dc1fb6002b261ffe5461c7 Mon Sep 17 00:00:00 2001 From: hongwei Date: Thu, 27 Jun 2024 18:08:14 +0200 Subject: [PATCH 71/75] feature/OBPv510 new version create view endpoint- step4 --- .../code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala | 5 +++-- obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala | 2 +- .../src/main/scala/code/api/v5_1_0/JSONFactory5.1.0.scala | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala b/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala index bafd14081..ce8dbc06b 100644 --- a/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala +++ b/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala @@ -52,6 +52,7 @@ import scala.collection.immutable.List */ object SwaggerDefinitionsJSON { + implicit def convertStringToBoolean(value:String) = value.toBoolean lazy val regulatedEntitiesJsonV510: RegulatedEntitiesJsonV510 = RegulatedEntitiesJsonV510(List(regulatedEntityJsonV510)) lazy val regulatedEntityJsonV510: RegulatedEntityJsonV510 = RegulatedEntityJsonV510( @@ -340,7 +341,7 @@ object SwaggerDefinitionsJSON { description = viewDescriptionExample.value, metadata_view =viewDescriptionExample.value, is_public = isPublicExample.value, - alias = whichAliasToUseExample.value, + which_alias_to_use = whichAliasToUseExample.value, hide_metadata_if_alias_used = hideMetadataIfAliasUsedExample.value.toBoolean, allowed_actions = allowedActionsV500, // Version 5.0.0 @@ -352,7 +353,7 @@ object SwaggerDefinitionsJSON { description = viewDescriptionExample.value, metadata_view = metadataViewExample.value, is_public = isPublicExample.value, - alias = whichAliasToUseExample.value, + which_alias_to_use = whichAliasToUseExample.value, hide_metadata_if_alias_used = hideMetadataIfAliasUsedExample.value.toBoolean, allowed_permissions = allowedActionsV500 ) diff --git a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala index 169c7e2c1..8dcb58e8b 100644 --- a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala +++ b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala @@ -2577,7 +2577,7 @@ trait APIMethods510 { isValidCustomViewId(targetViewId.value) } permissionsFromSource = APIUtil.getViewPermissions(view.asInstanceOf[ViewDefinition]) - permissionsFromTarget = targetCreateCustomViewJson.allowed_actions + permissionsFromTarget = targetCreateCustomViewJson.allowed_permissions _ <- Helper.booleanToFuture(failMsg = SourceViewHasLessPermission + s"Current source view permissions ($permissionsFromSource), target view permissions ($permissionsFromTarget)", cc = callContext) { permissionsFromTarget.toSet.subsetOf(permissionsFromSource) diff --git a/obp-api/src/main/scala/code/api/v5_1_0/JSONFactory5.1.0.scala b/obp-api/src/main/scala/code/api/v5_1_0/JSONFactory5.1.0.scala index c1ca3d486..a7c047658 100644 --- a/obp-api/src/main/scala/code/api/v5_1_0/JSONFactory5.1.0.scala +++ b/obp-api/src/main/scala/code/api/v5_1_0/JSONFactory5.1.0.scala @@ -358,7 +358,7 @@ case class UpdateCustomViewJson( is_public: Boolean, which_alias_to_use: String, hide_metadata_if_alias_used: Boolean, - allowed_actions: List[String] + allowed_permissions: List[String] ) { def toUpdateViewJson = UpdateViewJSON( description: String, @@ -367,7 +367,7 @@ case class UpdateCustomViewJson( is_firehose= None, which_alias_to_use: String, hide_metadata_if_alias_used: Boolean, - allowed_actions: List[String] + allowed_actions = allowed_permissions ) } From 434eb80d22f7cc85e4b51f53f6cc871974276e07 Mon Sep 17 00:00:00 2001 From: hongwei Date: Fri, 28 Jun 2024 09:20:05 +0200 Subject: [PATCH 72/75] feature/OBPv510 new version create view endpoint- step5 --- .../src/main/scala/code/api/util/ErrorMessages.scala | 12 ++++++------ obp-api/src/main/scala/code/api/util/NewStyle.scala | 2 +- .../main/scala/code/api/v4_0_0/APIMethods400.scala | 6 +++--- obp-api/src/main/scala/code/views/MapperViews.scala | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/obp-api/src/main/scala/code/api/util/ErrorMessages.scala b/obp-api/src/main/scala/code/api/util/ErrorMessages.scala index 985c34f07..2d07d11df 100644 --- a/obp-api/src/main/scala/code/api/util/ErrorMessages.scala +++ b/obp-api/src/main/scala/code/api/util/ErrorMessages.scala @@ -198,7 +198,7 @@ object ErrorMessages { s"OBP-20048: If target viewId is system view, the current view.can_revoke_access_to_views does not contains it. Or" + s"if target viewId is custom view, the current view.can_revoke_access_to_custom_views is false." - val SourceViewHasLessPermission = "OBP-20050: Source view contains less permissions than target view." + val SourceViewHasLessPermission = "OBP-20049: Source view contains less permissions than target view." val UserNotSuperAdmin = "OBP-20050: Current User is not a Super Admin!" @@ -408,8 +408,8 @@ object ErrorMessages { val ApiCollectionEndpointNotFound = "OBP-30082: ApiCollectionEndpoint not found." val CreateApiCollectionEndpointError = "OBP-30083: Could not create ApiCollectionEndpoint." val DeleteApiCollectionEndpointError = "OBP-30084: Could not delete ApiCollectionEndpoint." - val ApiCollectionEndpointAlreadyExisting = "OBP-30085: The ApiCollectionEndpoint is already Existing." - val ApiCollectionAlreadyExisting = "OBP-30086: The ApiCollection is already Existing." + val ApiCollectionEndpointAlreadyExists = "OBP-30085: The ApiCollectionEndpoint is already exists." + val ApiCollectionAlreadyExists = "OBP-30086: The ApiCollection is already exists." val DoubleEntryTransactionNotFound = "OBP-30087: Double Entry Transaction not found." @@ -501,7 +501,7 @@ object ErrorMessages { val DeleteSystemViewError = "OBP-30251: Could not delete the system view" val SystemViewNotFound = "OBP-30252: System view not found. Please specify a valid value for VIEW_ID" val UpdateSystemViewError = "OBP-30253: Could not update the system view" - val ExistingSystemViewError = "OBP-30254: The system view is already Existing." + val SystemViewAlreadyExistsError = "OBP-30254: The system view is already exists." val EmptyNameOfSystemViewError = "OBP-30255: You cannot create a View with an empty Name" val DeleteCustomViewError = "OBP-30256: Could not delete the custom view" val CannotFindCustomViewError = "OBP-30257: Could not find the custom view" @@ -513,7 +513,7 @@ object ErrorMessages { val GetCounterpartyLimitError = "OBP-30263: Counterparty limit not found. Please specify a valid value for BANK_ID, ACCOUNT_ID, VIEW_ID or COUNTERPARTY_ID." val CounterpartyLimitAlreadyExists = "OBP-30264: Counterparty limit already exists. Please specify a different value for BANK_ID, ACCOUNT_ID, VIEW_ID or COUNTERPARTY_ID." val DeleteCounterpartyLimitError = "OBP-30265: Could not delete the counterparty limit." - val ExistingCustomViewError = "OBP-30266: The custom view is already Existing." + val CustomViewAlreadyExistsError = "OBP-30266: The custom view is already exists." val TaxResidenceNotFound = "OBP-30300: Tax Residence not found by TAX_RESIDENCE_ID. " val CustomerAddressNotFound = "OBP-30310: Customer's Address not found by CUSTOMER_ADDRESS_ID. " @@ -734,7 +734,7 @@ object ErrorMessages { // MethodRouting Exceptions (OBP-7XXXX) val InvalidBankIdRegex = "OBP-70001: Incorrect regex for bankIdPattern." val MethodRoutingNotFoundByMethodRoutingId = "OBP-70002: MethodRouting not found. Please specify a valid value for method_routing_id." - val ExistingMethodRoutingError = "OBP-70003: Method Routing is already existing." + val MethodRoutingAlreadyExistsError = "OBP-70003: Method Routing is already exists." // Cascade Deletion Exceptions (OBP-8XXXX) val CouldNotDeleteCascade = "OBP-80001: Could not delete cascade." diff --git a/obp-api/src/main/scala/code/api/util/NewStyle.scala b/obp-api/src/main/scala/code/api/util/NewStyle.scala index a086dfe26..c7c488e49 100644 --- a/obp-api/src/main/scala/code/api/util/NewStyle.scala +++ b/obp-api/src/main/scala/code/api/util/NewStyle.scala @@ -2923,7 +2923,7 @@ object NewStyle extends MdcLoggable{ } val notExists = if(exists) Empty else Full(true) - (unboxFullOrFail(notExists, callContext, s"$ExistingMethodRoutingError Please modify the following parameters:" + + (unboxFullOrFail(notExists, callContext, s"$MethodRoutingAlreadyExistsError Please modify the following parameters:" + s"is_bank_id_exact_match(${methodRouting.isBankIdExactMatch}), " + s"method_name(${methodRouting.methodName}), " + s"bank_id_pattern(${methodRouting.bankIdPattern.getOrElse("")})" diff --git a/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala b/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala index 0dafbb217..5201455aa 100644 --- a/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala +++ b/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala @@ -9016,7 +9016,7 @@ trait APIMethods400 extends MdcLoggable { json.extract[PostApiCollectionJson400] } apiCollection <- Future{MappedApiCollectionsProvider.getApiCollectionByUserIdAndCollectionName(cc.userId, postJson.api_collection_name)} - _ <- Helper.booleanToFuture(failMsg = s"$ApiCollectionAlreadyExisting Current api_collection_name(${postJson.api_collection_name}) is already existing for the log in user.", cc=cc.callContext) { + _ <- Helper.booleanToFuture(failMsg = s"$ApiCollectionAlreadyExists Current api_collection_name(${postJson.api_collection_name}) is already existing for the log in user.", cc=cc.callContext) { apiCollection.isEmpty } (apiCollection, callContext) <- NewStyle.function.createApiCollection( @@ -9299,7 +9299,7 @@ trait APIMethods400 extends MdcLoggable { } (apiCollection, callContext) <- NewStyle.function.getApiCollectionByUserIdAndCollectionName(cc.userId, apiCollectionName, Some(cc)) apiCollectionEndpoint <- Future{MappedApiCollectionEndpointsProvider.getApiCollectionEndpointByApiCollectionIdAndOperationId(apiCollection.apiCollectionId, postJson.operation_id)} - _ <- Helper.booleanToFuture(failMsg = s"$ApiCollectionEndpointAlreadyExisting Current OPERATION_ID(${postJson.operation_id}) is already in API_COLLECTION_NAME($apiCollectionName) ", cc=callContext) { + _ <- Helper.booleanToFuture(failMsg = s"$ApiCollectionEndpointAlreadyExists Current OPERATION_ID(${postJson.operation_id}) is already in API_COLLECTION_NAME($apiCollectionName) ", cc=callContext) { apiCollectionEndpoint.isEmpty } (apiCollectionEndpoint, callContext) <- NewStyle.function.createApiCollectionEndpoint( @@ -9348,7 +9348,7 @@ trait APIMethods400 extends MdcLoggable { } (apiCollection, callContext) <- NewStyle.function.getApiCollectionById(apiCollectionId, Some(cc)) apiCollectionEndpoint <- Future{MappedApiCollectionEndpointsProvider.getApiCollectionEndpointByApiCollectionIdAndOperationId(apiCollection.apiCollectionId, postJson.operation_id)} - _ <- Helper.booleanToFuture(failMsg = s"$ApiCollectionEndpointAlreadyExisting Current OPERATION_ID(${postJson.operation_id}) is already in API_COLLECTION_ID($apiCollectionId) ", cc=callContext) { + _ <- Helper.booleanToFuture(failMsg = s"$ApiCollectionEndpointAlreadyExists Current OPERATION_ID(${postJson.operation_id}) is already in API_COLLECTION_ID($apiCollectionId) ", cc=callContext) { apiCollectionEndpoint.isEmpty } (apiCollectionEndpoint, callContext) <- NewStyle.function.createApiCollectionEndpoint( diff --git a/obp-api/src/main/scala/code/views/MapperViews.scala b/obp-api/src/main/scala/code/views/MapperViews.scala index 36270c7fc..9837512ce 100644 --- a/obp-api/src/main/scala/code/views/MapperViews.scala +++ b/obp-api/src/main/scala/code/views/MapperViews.scala @@ -399,7 +399,7 @@ object MapperViews extends Views with MdcLoggable { existing match { case true => - Failure(s"$ExistingSystemViewError Current VIEW_ID($viewId)") + Failure(s"$SystemViewAlreadyExistsError Current VIEW_ID($viewId)") case false => val createdView = ViewDefinition.create.name_(view.name).view_id(viewId) createdView.setFromViewData(view) @@ -436,7 +436,7 @@ object MapperViews extends Views with MdcLoggable { ) == 1 if (existing) - Failure(s"$ExistingCustomViewError Current BankId(${bankAccountId.bankId.value}), AccountId(${bankAccountId.accountId.value}), ViewId($viewId).") + Failure(s"$CustomViewAlreadyExistsError Current BankId(${bankAccountId.bankId.value}), AccountId(${bankAccountId.accountId.value}), ViewId($viewId).") else { val createdView = ViewDefinition.create. name_(view.name). From 3e14b4c4d6f02f4313c750c3126df57e3ee953b0 Mon Sep 17 00:00:00 2001 From: hongwei Date: Fri, 28 Jun 2024 09:54:26 +0200 Subject: [PATCH 73/75] refactor/added missing endpoints --- .../src/test/scala/code/api/v5_1_0/CounterpartyLimitTest.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/obp-api/src/test/scala/code/api/v5_1_0/CounterpartyLimitTest.scala b/obp-api/src/test/scala/code/api/v5_1_0/CounterpartyLimitTest.scala index 7c7c5adf1..3e9516539 100644 --- a/obp-api/src/test/scala/code/api/v5_1_0/CounterpartyLimitTest.scala +++ b/obp-api/src/test/scala/code/api/v5_1_0/CounterpartyLimitTest.scala @@ -38,7 +38,7 @@ class CounterpartyLimitTest extends V510ServerSetup { ) - feature(s"test $ApiEndpoint1 Authorized access") { + feature(s"test $ApiEndpoint1,$ApiEndpoint2, $ApiEndpoint3, $ApiEndpoint4, Authorized access") { scenario("We will call the endpoint without user credentials", ApiEndpoint1, ApiEndpoint2,ApiEndpoint3,ApiEndpoint4,VersionOfApi) { val counterparty = createCounterparty(bankId, accountId, accountId, true, UUID.randomUUID.toString); From 25b400fa330cfbe90775c8a5fdc8ded190124c54 Mon Sep 17 00:00:00 2001 From: hongwei Date: Fri, 28 Jun 2024 13:14:56 +0200 Subject: [PATCH 74/75] feature/OBPv510 new version create view endpoint- step6 --- .../scala/code/api/v5_1_0/APIMethods510.scala | 4 +- .../code/api/v5_1_0/CustomViewTest.scala | 231 ++++++++++++++++++ 2 files changed, 233 insertions(+), 2 deletions(-) create mode 100644 obp-api/src/test/scala/code/api/v5_1_0/CustomViewTest.scala diff --git a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala index 8dcb58e8b..454e3945e 100644 --- a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala +++ b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala @@ -2522,7 +2522,7 @@ trait APIMethods510 { permissionsFromSource = APIUtil.getViewPermissions(view.asInstanceOf[ViewDefinition]) permissionsFromTarget = createCustomViewJson.allowed_permissions - _ <- Helper.booleanToFuture(failMsg = SourceViewHasLessPermission + s"Current source view permissions ($permissionsFromSource), target view permissions ($permissionsFromTarget)", cc = callContext) { + _ <- Helper.booleanToFuture(failMsg = SourceViewHasLessPermission + s"Current source viewId($viewId) permissions ($permissionsFromSource), target viewName${createCustomViewJson.name} permissions ($permissionsFromTarget)", cc = callContext) { permissionsFromTarget.toSet.subsetOf(permissionsFromSource) } @@ -2583,7 +2583,7 @@ trait APIMethods510 { permissionsFromTarget.toSet.subsetOf(permissionsFromSource) } - failmsg = s"${ErrorMessages.ViewDoesNotPermitAccess} You need the `${StringHelpers.snakify(nameOf(view.canCreateCustomView))}` permission on VIEW_ID(${viewId.value})" + failmsg = s"${ErrorMessages.ViewDoesNotPermitAccess} You need the `${StringHelpers.snakify(nameOf(view.canUpdateCustomView))}` permission on VIEW_ID(${viewId.value})" _ <- Helper.booleanToFuture(failmsg, cc = callContext) { view.canCreateCustomView diff --git a/obp-api/src/test/scala/code/api/v5_1_0/CustomViewTest.scala b/obp-api/src/test/scala/code/api/v5_1_0/CustomViewTest.scala new file mode 100644 index 000000000..2c03414fb --- /dev/null +++ b/obp-api/src/test/scala/code/api/v5_1_0/CustomViewTest.scala @@ -0,0 +1,231 @@ +package code.api.v5_1_0 + +import code.api.Constant.{SYSTEM_AUDITOR_VIEW_ID, SYSTEM_MANAGE_CUSTOM_VIEWS_VIEW_ID, SYSTEM_OWNER_VIEW_ID, SYSTEM_STAGE_ONE_VIEW_ID} +import code.api.util.APIUtil.OAuth._ +import code.api.util.ErrorMessages._ +import code.api.v5_1_0.OBPAPI5_1_0.Implementations5_1_0 +import com.github.dwickern.macros.NameOf.nameOf +import com.openbankproject.commons.model.ErrorMessage +import com.openbankproject.commons.util.ApiVersion +import net.liftweb.json.Serialization.write +import net.liftweb.util.StringHelpers +import org.scalatest.Tag + +import java.util.UUID + +class CustomViewTest extends V510ServerSetup { + /** + * Test tags + * Example: To run tests with tag "getPermissions": + * mvn test -D tagsToInclude + * + * This is made possible by the scalatest maven plugin + */ + object VersionOfApi extends Tag(ApiVersion.v5_1_0.toString) + object ApiEndpoint1 extends Tag(nameOf(Implementations5_1_0.createCustomView)) + object ApiEndpoint2 extends Tag(nameOf(Implementations5_1_0.updateCustomView)) + object ApiEndpoint3 extends Tag(nameOf(Implementations5_1_0.getCustomView)) + object ApiEndpoint4 extends Tag(nameOf(Implementations5_1_0.deleteCustomView)) + + + val bankId = testBankId1.value + val accountId = testAccountId1.value + val ownerView = SYSTEM_OWNER_VIEW_ID + val manageCustomView = SYSTEM_MANAGE_CUSTOM_VIEWS_VIEW_ID + val targetViewId = "_test" + + val postCustomViewJsonWithFullPermissinos = code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON.createCustomViewJson.copy(name=targetViewId) + val postCustomViewJson = code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON.createCustomViewJson.copy( + name=targetViewId, + metadata_view=targetViewId, + allowed_permissions = List("can_see_transaction_start_date", "can_add_url") + ) + val putCustomViewJsonWithFullPermissinos = code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON.updateCustomViewJson + val putCustomViewJson = code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON.updateCustomViewJson.copy( + description = "1", + metadata_view = "2", + is_public = false, + which_alias_to_use = "",//TODO please check this field later. + hide_metadata_if_alias_used = false, + allowed_permissions = List("can_see_transaction_this_bank_account", "can_see_bank_account_owners") + ) + + feature(s"test Authorized access") { + + scenario(s"We will call the endpoint, $UserNotLoggedIn", ApiEndpoint1, ApiEndpoint2, ApiEndpoint3, ApiEndpoint4, VersionOfApi) { + When("We make a request v5.1.0") + val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId / "views" / ownerView /"target-views").POST + val response510 = makePostRequest(request510, write(postCustomViewJson)) + Then("We should get a 401") + response510.code should equal(401) + response510.body.extract[ErrorMessage].message should equal(UserNotLoggedIn) + + { + + val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId /"views" / ownerView /"target-views" / targetViewId ).PUT + val response510 = makePutRequest(request510, write(putCustomViewJson)) + Then("We should get a 401") + response510.code should equal(401) + response510.body.extract[ErrorMessage].message should equal(UserNotLoggedIn) + + } + { + val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId /"views" / ownerView /"target-views" / targetViewId ).GET + val response510 = makeGetRequest(request510) + Then("We should get a 401") + response510.code should equal(401) + response510.body.extract[ErrorMessage].message should equal(UserNotLoggedIn) + + } + { + val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId /"views" / ownerView /"target-views" / targetViewId ).DELETE + val response510 = makeDeleteRequest(request510) + Then("We should get a 401") + response510.code should equal(401) + response510.body.extract[ErrorMessage].message should equal(UserNotLoggedIn) + + } + } + + scenario(s"We will call the endpoint, $SourceViewHasLessPermission", ApiEndpoint1, ApiEndpoint2, ApiEndpoint3, ApiEndpoint4, VersionOfApi) { + + When("We make a request v5.1.0") + val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId / "views" / ownerView /"target-views").POST <@ (user1) + val response510 = makePostRequest(request510, write(postCustomViewJsonWithFullPermissinos)) + Then("We should get a 400") + response510.code should equal(400) + response510.body.extract[ErrorMessage].message contains(SourceViewHasLessPermission) shouldBe(true) + + { + val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId /"views" / ownerView /"target-views" / targetViewId ).PUT <@ (user1) + val response510 = makePutRequest(request510, write(putCustomViewJsonWithFullPermissinos)) + Then("We should get a 400") + response510.code should equal(400) + response510.body.extract[ErrorMessage].message contains(SourceViewHasLessPermission) shouldBe(true) + } + } + + scenario(s"We will call the endpoint, $ViewDoesNotPermitAccess ", ApiEndpoint1, ApiEndpoint2, ApiEndpoint3, ApiEndpoint4, VersionOfApi) { + + When("We make a request v5.1.0") + val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId / "views" / ownerView /"target-views").POST <@ (user1) + val response510 = makePostRequest(request510, write(postCustomViewJson)) + Then("We should get a 400") + response510.code should equal(400) + response510.body.extract[ErrorMessage].message contains (ViewDoesNotPermitAccess) shouldBe (true) + response510.body.extract[ErrorMessage].message contains ("can_create_custom_view") shouldBe (true) + + { + + val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId /"views" / ownerView /"target-views" / targetViewId ).PUT <@ (user1) + val response510 = makePutRequest(request510, write(putCustomViewJson)) + Then("We should get a 400") + response510.code should equal(400) + response510.body.extract[ErrorMessage].message contains (ViewDoesNotPermitAccess) shouldBe (true) + response510.body.extract[ErrorMessage].message contains ("can_update_custom_view") shouldBe (true) + } + + { + When("we need to prepare the custom view for get and delete") + val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId / "views" / manageCustomView / "target-views").POST <@ (user1) + val response510 = makePostRequest(request510, write(postCustomViewJson)) + Then("We should get a 201") + response510.code should equal(201) + } + { + + val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId /"views" / SYSTEM_AUDITOR_VIEW_ID /"target-views" / targetViewId ).GET <@ (user1) + val response510 = makeGetRequest(request510) + Then("We should get a 400") + response510.code should equal(400) + response510.body.extract[ErrorMessage].message contains (ViewDoesNotPermitAccess) shouldBe (true) + response510.body.extract[ErrorMessage].message contains ("can_see_available_views_for_bank_account") shouldBe (true) + + } + { + val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId / "views" / ownerView / "target-views" / targetViewId).DELETE <@ (user1) + val response510 = makeDeleteRequest(request510) + Then("We should get a 400") + response510.code should equal(400) + response510.body.extract[ErrorMessage].message contains (ViewDoesNotPermitAccess) shouldBe (true) + response510.body.extract[ErrorMessage].message contains ("can_delete_custom_view") shouldBe (true) + + } + } + + scenario("We will call the endpoint with user credentials", ApiEndpoint1, ApiEndpoint2, ApiEndpoint3, ApiEndpoint4, VersionOfApi) { + + When("We make a request v5.1.0") + val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId / "views" / manageCustomView /"target-views").POST <@ (user1) + val response510 = makePostRequest(request510, write(postCustomViewJson)) + Then("We should get a 201") + response510.code should equal(201) + response510.body.extract[CustomViewJsonV510].name should equal(postCustomViewJson.name) + response510.body.extract[CustomViewJsonV510].description should equal(postCustomViewJson.description) + response510.body.extract[CustomViewJsonV510].metadata_view should equal(postCustomViewJson.metadata_view) + response510.body.extract[CustomViewJsonV510].is_public should equal(postCustomViewJson.is_public) + response510.body.extract[CustomViewJsonV510].alias should equal(postCustomViewJson.which_alias_to_use) + response510.body.extract[CustomViewJsonV510].hide_metadata_if_alias_used should equal(postCustomViewJson.hide_metadata_if_alias_used) + response510.body.extract[CustomViewJsonV510].allowed_permissions.sorted should equal(postCustomViewJson.allowed_permissions.sorted) + + { + val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId / "views" / ownerView / "target-views" / targetViewId).GET <@ (user1) + val response510 = makeGetRequest(request510) + Then("We should get a 200") + response510.code should equal(200) + response510.body.extract[CustomViewJsonV510].name should equal(postCustomViewJson.name) + response510.body.extract[CustomViewJsonV510].description should equal(postCustomViewJson.description) + response510.body.extract[CustomViewJsonV510].metadata_view should equal(postCustomViewJson.metadata_view) + response510.body.extract[CustomViewJsonV510].is_public should equal(postCustomViewJson.is_public) + response510.body.extract[CustomViewJsonV510].alias should equal(postCustomViewJson.which_alias_to_use) + response510.body.extract[CustomViewJsonV510].hide_metadata_if_alias_used should equal(postCustomViewJson.hide_metadata_if_alias_used) + response510.body.extract[CustomViewJsonV510].allowed_permissions.sorted should equal(postCustomViewJson.allowed_permissions.sorted) + + } + + { + + val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId /"views" / manageCustomView /"target-views" / targetViewId ).PUT <@ (user1) + val response510 = makePutRequest(request510, write(putCustomViewJson)) + Then("We should get a 200") + response510.code should equal(200) + response510.body.extract[CustomViewJsonV510].description should equal(putCustomViewJson.description) + response510.body.extract[CustomViewJsonV510].metadata_view should equal(putCustomViewJson.metadata_view) + response510.body.extract[CustomViewJsonV510].is_public should equal(putCustomViewJson.is_public) + response510.body.extract[CustomViewJsonV510].alias should equal(putCustomViewJson.which_alias_to_use) + response510.body.extract[CustomViewJsonV510].hide_metadata_if_alias_used should equal(putCustomViewJson.hide_metadata_if_alias_used) + response510.body.extract[CustomViewJsonV510].allowed_permissions.sorted should equal(putCustomViewJson.allowed_permissions.sorted) + + } + { + val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId /"views" / ownerView /"target-views" / targetViewId ).GET <@ (user1) + val response510 = makeGetRequest(request510) + Then("We should get a 200") + response510.code should equal(200) + response510.body.extract[CustomViewJsonV510].description should equal(putCustomViewJson.description) + response510.body.extract[CustomViewJsonV510].metadata_view should equal(putCustomViewJson.metadata_view) + response510.body.extract[CustomViewJsonV510].is_public should equal(putCustomViewJson.is_public) + response510.body.extract[CustomViewJsonV510].alias should equal(putCustomViewJson.which_alias_to_use) + response510.body.extract[CustomViewJsonV510].hide_metadata_if_alias_used should equal(putCustomViewJson.hide_metadata_if_alias_used) + response510.body.extract[CustomViewJsonV510].allowed_permissions.sorted should equal(putCustomViewJson.allowed_permissions.sorted) + + } + { + val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId /"views" / manageCustomView /"target-views" / targetViewId ).DELETE <@ (user1) + val response510 = makeDeleteRequest(request510) + Then("We should get a 204") + response510.code should equal(204) + } + { + val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId / "views" / ownerView / "target-views" / targetViewId).GET <@ (user1) + val response510 = makeGetRequest(request510) + Then("We should get a 400") + response510.code should equal(400) + response510.body.extract[ErrorMessage].message contains (ViewNotFound) shouldBe(true) + + } + } + + } + +} From 6ea76fec86edaf887699a6693300ab4b8df49818 Mon Sep 17 00:00:00 2001 From: hongwei Date: Fri, 28 Jun 2024 19:33:53 +0200 Subject: [PATCH 75/75] test/fixed the failed test --- .../scala/code/api/util/ExampleValue.scala | 88 +++++++++---------- 1 file changed, 44 insertions(+), 44 deletions(-) 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 3293906ad..9a1eb2701 100644 --- a/obp-api/src/main/scala/code/api/util/ExampleValue.scala +++ b/obp-api/src/main/scala/code/api/util/ExampleValue.scala @@ -25,7 +25,7 @@ object ExampleValue { val NoDescriptionProvided = "no-description-provided" val NoExampleProvided = "" - val booleanTrue = "true" + val booleanFalse = "false" lazy val bankIdGlossary = glossaryItems.find(_.title == "Bank.bank_id").map(_.textDescription) @@ -104,7 +104,7 @@ object ExampleValue { lazy val dependentsExample = ConnectorField("2", s"the number of dependents") // Dominant form in American English glossaryItems += makeGlossaryItem("Customer.dependents", dependentsExample) - lazy val kycStatusExample = ConnectorField(booleanTrue, s"This is boolean to indicate if the cusomter's KYC has been checked.") + lazy val kycStatusExample = ConnectorField(booleanFalse, s"This is boolean to indicate if the cusomter's KYC has been checked.") glossaryItems += makeGlossaryItem("Customer.kycStatus", kycStatusExample) lazy val urlExample = ConnectorField("http://www.example.com/id-docs/123/image.png", s"The URL ") @@ -178,7 +178,7 @@ object ExampleValue { lazy val otherAccountProviderExample = ConnectorField("", s"")//TODO, not sure what is this field for? glossaryItems += makeGlossaryItem("Transaction.otherAccountProvider", otherAccountProviderExample) - lazy val isBeneficiaryExample = ConnectorField(booleanTrue, s"This is a boolean. True if the originAccount can send money to the Counterparty") + lazy val isBeneficiaryExample = ConnectorField(booleanFalse, s"This is a boolean. True if the originAccount can send money to the Counterparty") glossaryItems += makeGlossaryItem("Counterparty.isBeneficiary", isBeneficiaryExample) lazy val counterpartyNameExample = ConnectorField("John Smith Ltd.", s"The name of a Counterparty. Ideally unique for an Account") @@ -559,7 +559,7 @@ object ExampleValue { lazy val inboundAvroSchemaExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) glossaryItems += makeGlossaryItem("DynamicMessageDoc.inboundAvroSchema", inboundAvroSchemaExample) - lazy val canSeeImagesExample = ConnectorField(booleanTrue,NoDescriptionProvided) + lazy val canSeeImagesExample = ConnectorField(booleanFalse,NoDescriptionProvided) glossaryItems += makeGlossaryItem("can_see_images", canSeeImagesExample) lazy val topConsumersExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) @@ -571,7 +571,7 @@ object ExampleValue { lazy val maximumResponseTimeExample = ConnectorField("60",NoDescriptionProvided) glossaryItems += makeGlossaryItem("maximum_response_time", maximumResponseTimeExample) - lazy val cancelledExample = ConnectorField(booleanTrue,NoDescriptionProvided) + lazy val cancelledExample = ConnectorField(booleanFalse,NoDescriptionProvided) glossaryItems += makeGlossaryItem("cancelled", cancelledExample) lazy val entitlementRequestsExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) @@ -619,7 +619,7 @@ object ExampleValue { lazy val canSeeOtherAccountRoutingSchemeExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) glossaryItems += makeGlossaryItem("can_see_other_account_routing_scheme", canSeeOtherAccountRoutingSchemeExample) - lazy val canDeleteCorporateLocationExample = ConnectorField(booleanTrue,NoDescriptionProvided) + lazy val canDeleteCorporateLocationExample = ConnectorField(booleanFalse,NoDescriptionProvided) glossaryItems += makeGlossaryItem("can_delete_corporate_location", canDeleteCorporateLocationExample) lazy val fromExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) @@ -691,7 +691,7 @@ object ExampleValue { lazy val corporateLocationExample = ConnectorField("10",NoDescriptionProvided) glossaryItems += makeGlossaryItem("corporate_location", corporateLocationExample) - lazy val enabledExample = ConnectorField(booleanTrue,NoDescriptionProvided) + lazy val enabledExample = ConnectorField(booleanFalse,NoDescriptionProvided) glossaryItems += makeGlossaryItem("enabled", enabledExample) lazy val durationExample = ConnectorField("5.123"," This is a decimal number in seconds, eg: 1 for 1 second, 0.001 for 1 ms") @@ -703,7 +703,7 @@ object ExampleValue { lazy val toSepaExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) glossaryItems += makeGlossaryItem("to_sepa", toSepaExample) - lazy val whichAliasToUseExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) + lazy val whichAliasToUseExample = ConnectorField("public",NoDescriptionProvided) glossaryItems += makeGlossaryItem("which_alias_to_use", whichAliasToUseExample) lazy val canAddImageExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) @@ -772,7 +772,7 @@ object ExampleValue { lazy val creatorExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) glossaryItems += makeGlossaryItem("creator", creatorExample) - lazy val activeExample = ConnectorField(booleanTrue,NoDescriptionProvided) + lazy val activeExample = ConnectorField(booleanFalse,NoDescriptionProvided) glossaryItems += makeGlossaryItem("active", activeExample) lazy val canSeeOtherAccountMetadataExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) @@ -964,7 +964,7 @@ object ExampleValue { lazy val canSeeTransactionFinishDateExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) glossaryItems += makeGlossaryItem("can_see_transaction_finish_date", canSeeTransactionFinishDateExample) - lazy val satisfiedExample = ConnectorField(booleanTrue,NoDescriptionProvided) + lazy val satisfiedExample = ConnectorField(booleanFalse,NoDescriptionProvided) glossaryItems += makeGlossaryItem("satisfied", satisfiedExample) lazy val canSeeOtherAccountIbanExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) @@ -1171,7 +1171,7 @@ object ExampleValue { lazy val accountOtpExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) glossaryItems += makeGlossaryItem("account_otp", accountOtpExample) - lazy val hideMetadataIfAliasUsedExample = ConnectorField("true",NoDescriptionProvided) + lazy val hideMetadataIfAliasUsedExample = ConnectorField(booleanFalse, NoDescriptionProvided) glossaryItems += makeGlossaryItem("hide_metadata_if_alias_used", hideMetadataIfAliasUsedExample) lazy val canSeeBankAccountCurrencyExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) @@ -1575,7 +1575,7 @@ object ExampleValue { lazy val isFirehoseExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) glossaryItems += makeGlossaryItem("is_firehose", isFirehoseExample) - lazy val okExample = ConnectorField(booleanTrue,NoDescriptionProvided) + lazy val okExample = ConnectorField(booleanFalse,NoDescriptionProvided) glossaryItems += makeGlossaryItem("ok", okExample) lazy val bankRoutingExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) @@ -1599,7 +1599,7 @@ object ExampleValue { lazy val dependentEndpointsExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) glossaryItems += makeGlossaryItem("dependent_endpoints", dependentEndpointsExample) - lazy val hasDepositCapabilityExample = ConnectorField(booleanTrue,NoDescriptionProvided) + lazy val hasDepositCapabilityExample = ConnectorField(booleanFalse,NoDescriptionProvided) glossaryItems += makeGlossaryItem("ATM.has_deposit_capability", hasDepositCapabilityExample) lazy val toCounterpartyExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) @@ -1623,10 +1623,10 @@ object ExampleValue { lazy val canSeeCommentsExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) glossaryItems += makeGlossaryItem("can_see_comments", canSeeCommentsExample) - lazy val canEditOwnerCommentExample = ConnectorField(booleanTrue,NoDescriptionProvided) + lazy val canEditOwnerCommentExample = ConnectorField(booleanFalse,NoDescriptionProvided) glossaryItems += makeGlossaryItem("can_edit_owner_comment", canEditOwnerCommentExample) - lazy val canAddCounterpartyExample = ConnectorField(booleanTrue,NoDescriptionProvided) + lazy val canAddCounterpartyExample = ConnectorField(booleanFalse,NoDescriptionProvided) glossaryItems += makeGlossaryItem("can_add_counterparty", canAddCounterpartyExample) lazy val markdownExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) @@ -1659,7 +1659,7 @@ object ExampleValue { lazy val narrativeExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) glossaryItems += makeGlossaryItem("narrative", narrativeExample) - lazy val canSeeOtherAccountRoutingAddressExample = ConnectorField(booleanTrue,NoDescriptionProvided) + lazy val canSeeOtherAccountRoutingAddressExample = ConnectorField(booleanFalse,NoDescriptionProvided) glossaryItems += makeGlossaryItem("can_see_other_account_routing_address", canSeeOtherAccountRoutingAddressExample) lazy val statusesExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) @@ -1695,7 +1695,7 @@ object ExampleValue { lazy val tuesdayExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) glossaryItems += makeGlossaryItem("tuesday", tuesdayExample) - lazy val canQueryAvailableFundsExample = ConnectorField(booleanTrue,NoDescriptionProvided) + lazy val canQueryAvailableFundsExample = ConnectorField(booleanFalse,NoDescriptionProvided) glossaryItems += makeGlossaryItem("can_query_available_funds", canQueryAvailableFundsExample) lazy val otherAccountSecondaryRoutingSchemeExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) @@ -1781,7 +1781,7 @@ object ExampleValue { lazy val roleExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) glossaryItems += makeGlossaryItem("role", roleExample) - lazy val requireScopesForListedRolesExample = ConnectorField(booleanTrue,NoDescriptionProvided) + lazy val requireScopesForListedRolesExample = ConnectorField(booleanFalse,NoDescriptionProvided) glossaryItems += makeGlossaryItem("require_scopes_for_listed_roles", requireScopesForListedRolesExample) lazy val branchTypeExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) @@ -1790,7 +1790,7 @@ object ExampleValue { lazy val fullNameExample = ConnectorField("full name string",NoDescriptionProvided) glossaryItems += makeGlossaryItem("full_name", fullNameExample) - lazy val canCreateDirectDebitExample = ConnectorField(booleanTrue,NoDescriptionProvided) + lazy val canCreateDirectDebitExample = ConnectorField(booleanFalse,NoDescriptionProvided) glossaryItems += makeGlossaryItem("can_create_direct_debit", canCreateDirectDebitExample) lazy val futureDateExample = ConnectorField("20200127",NoDescriptionProvided) @@ -1808,7 +1808,7 @@ object ExampleValue { lazy val documentNumberExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) glossaryItems += makeGlossaryItem("document_number", documentNumberExample) - lazy val canSeeOtherAccountNationalIdentifierExample = ConnectorField(booleanTrue,NoDescriptionProvided) + lazy val canSeeOtherAccountNationalIdentifierExample = ConnectorField(booleanFalse,NoDescriptionProvided) glossaryItems += makeGlossaryItem("can_see_other_account_national_identifier", canSeeOtherAccountNationalIdentifierExample) lazy val canSeeTransactionStartDateExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) @@ -1820,7 +1820,7 @@ object ExampleValue { lazy val cacheExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) glossaryItems += makeGlossaryItem("cache", cacheExample) - lazy val canSeeBankRoutingAddressExample = ConnectorField(booleanTrue,NoDescriptionProvided) + lazy val canSeeBankRoutingAddressExample = ConnectorField(booleanFalse,NoDescriptionProvided) glossaryItems += makeGlossaryItem("can_see_bank_routing_address", canSeeBankRoutingAddressExample) lazy val usersExample = ConnectorField("user list", "Please refer to the user object.") @@ -1832,7 +1832,7 @@ object ExampleValue { lazy val ktyExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) glossaryItems += makeGlossaryItem("kty", ktyExample) - lazy val canBeSeenOnViewsExample = ConnectorField(booleanTrue,NoDescriptionProvided) + lazy val canBeSeenOnViewsExample = ConnectorField(booleanFalse,NoDescriptionProvided) glossaryItems += makeGlossaryItem("can_be_seen_on_views", canBeSeenOnViewsExample) lazy val kidExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) @@ -1850,7 +1850,7 @@ object ExampleValue { lazy val metadataExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) glossaryItems += makeGlossaryItem("metadata", metadataExample) - lazy val canSeeTransactionAmountExample = ConnectorField(booleanTrue,NoDescriptionProvided) + lazy val canSeeTransactionAmountExample = ConnectorField(booleanFalse,NoDescriptionProvided) glossaryItems += makeGlossaryItem("can_see_transaction_amount", canSeeTransactionAmountExample) lazy val methodRoutingIdExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) @@ -1874,10 +1874,10 @@ object ExampleValue { lazy val countryCodeExample = ConnectorField("1254",NoDescriptionProvided) glossaryItems += makeGlossaryItem("country_code", countryCodeExample) - lazy val canSeeBankAccountCreditLimitExample = ConnectorField(booleanTrue,NoDescriptionProvided) + lazy val canSeeBankAccountCreditLimitExample = ConnectorField(booleanFalse,NoDescriptionProvided) glossaryItems += makeGlossaryItem("can_see_bank_account_credit_limit", canSeeBankAccountCreditLimitExample) - lazy val canSeeOtherAccountNumberExample = ConnectorField(booleanTrue,NoDescriptionProvided) + lazy val canSeeOtherAccountNumberExample = ConnectorField(booleanFalse,NoDescriptionProvided) glossaryItems += makeGlossaryItem("can_see_other_account_number", canSeeOtherAccountNumberExample) lazy val orderExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) @@ -1895,10 +1895,10 @@ object ExampleValue { lazy val taxResidenceExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) glossaryItems += makeGlossaryItem("tax_residence", taxResidenceExample) - lazy val isActiveExample = ConnectorField(booleanTrue,NoDescriptionProvided) + lazy val isActiveExample = ConnectorField(booleanFalse,NoDescriptionProvided) glossaryItems += makeGlossaryItem("is_active", isActiveExample) - lazy val canSeeBankAccountBankNameExample = ConnectorField(booleanTrue,NoDescriptionProvided) + lazy val canSeeBankAccountBankNameExample = ConnectorField(booleanFalse,NoDescriptionProvided) glossaryItems += makeGlossaryItem("can_see_bank_account_bank_name", canSeeBankAccountBankNameExample) lazy val firstNameExample = ConnectorField("Tom","The first name") @@ -1913,7 +1913,7 @@ object ExampleValue { lazy val transactionIdsExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) glossaryItems += makeGlossaryItem("transaction_ids", transactionIdsExample) - lazy val canSeeBankAccountOwnersExample = ConnectorField(booleanTrue,NoDescriptionProvided) + lazy val canSeeBankAccountOwnersExample = ConnectorField(booleanFalse,NoDescriptionProvided) glossaryItems += makeGlossaryItem("can_see_bank_account_owners", canSeeBankAccountOwnersExample) lazy val actualDateExample = ConnectorField("2020-01-27",NoDescriptionProvided) @@ -1922,10 +1922,10 @@ object ExampleValue { lazy val exampleOutboundMessageExample = ConnectorField("{}","this will the json object") glossaryItems += makeGlossaryItem("example_outbound_message", exampleOutboundMessageExample) - lazy val canDeleteWhereTagExample = ConnectorField(booleanTrue,NoDescriptionProvided) + lazy val canDeleteWhereTagExample = ConnectorField(booleanFalse,NoDescriptionProvided) glossaryItems += makeGlossaryItem("can_delete_where_tag", canDeleteWhereTagExample) - lazy val canSeeUrlExample = ConnectorField(booleanTrue,NoDescriptionProvided) + lazy val canSeeUrlExample = ConnectorField(booleanFalse,NoDescriptionProvided) glossaryItems += makeGlossaryItem("can_see_url", canSeeUrlExample) lazy val versionExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) @@ -1952,7 +1952,7 @@ object ExampleValue { lazy val implementedInVersionExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) glossaryItems += makeGlossaryItem("implemented_in_version", implementedInVersionExample) - lazy val canSeeImageUrlExample = ConnectorField(booleanTrue,NoDescriptionProvided) + lazy val canSeeImageUrlExample = ConnectorField(booleanFalse,NoDescriptionProvided) glossaryItems += makeGlossaryItem("can_see_image_url", canSeeImageUrlExample) lazy val toTransferToPhoneExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) @@ -1997,7 +1997,7 @@ object ExampleValue { lazy val eExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) glossaryItems += makeGlossaryItem("e", eExample) - lazy val canSeeCorporateLocationExample = ConnectorField(booleanTrue,NoDescriptionProvided) + lazy val canSeeCorporateLocationExample = ConnectorField(booleanFalse,NoDescriptionProvided) glossaryItems += makeGlossaryItem("can_see_corporate_location", canSeeCorporateLocationExample) lazy val userExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) @@ -2045,7 +2045,7 @@ object ExampleValue { lazy val requiredfieldinfoExample = ConnectorField("false",NoDescriptionProvided) glossaryItems += makeGlossaryItem("requiredfieldinfo", requiredfieldinfoExample) - lazy val canSeeWhereTagExample = ConnectorField(booleanTrue,NoDescriptionProvided) + lazy val canSeeWhereTagExample = ConnectorField(booleanFalse,NoDescriptionProvided) glossaryItems += makeGlossaryItem("can_see_where_tag", canSeeWhereTagExample) lazy val bankidExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) @@ -2108,10 +2108,10 @@ object ExampleValue { lazy val toSandboxTanExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) glossaryItems += makeGlossaryItem("to_sandbox_tan", toSandboxTanExample) - lazy val canAddTagExample = ConnectorField(booleanTrue,NoDescriptionProvided) + lazy val canAddTagExample = ConnectorField(booleanFalse,NoDescriptionProvided) glossaryItems += makeGlossaryItem("can_add_tag", canAddTagExample) - lazy val canSeeBankAccountLabelExample = ConnectorField(booleanTrue,NoDescriptionProvided) + lazy val canSeeBankAccountLabelExample = ConnectorField(booleanFalse,NoDescriptionProvided) glossaryItems += makeGlossaryItem("can_see_bank_account_label", canSeeBankAccountLabelExample) lazy val serviceAvailableExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) @@ -2135,7 +2135,7 @@ object ExampleValue { lazy val driveUpExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) glossaryItems += makeGlossaryItem("drive_up", driveUpExample) - lazy val canAddMoreInfoExample = ConnectorField(booleanTrue,NoDescriptionProvided) + lazy val canAddMoreInfoExample = ConnectorField(booleanFalse,NoDescriptionProvided) glossaryItems += makeGlossaryItem("can_add_more_info", canAddMoreInfoExample) lazy val detailExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) @@ -2165,13 +2165,13 @@ object ExampleValue { lazy val maxNumberOfYearlyTransactionsExample = ConnectorField("100",NoDescriptionProvided) glossaryItems += makeGlossaryItem("max_number_of_yearly_transactions", maxNumberOfYearlyTransactionsExample) - lazy val canAddImageUrlExample = ConnectorField(booleanTrue,NoDescriptionProvided) + lazy val canAddImageUrlExample = ConnectorField(booleanFalse,NoDescriptionProvided) glossaryItems += makeGlossaryItem("can_add_image_url", canAddImageUrlExample) lazy val jwksUrisExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) glossaryItems += makeGlossaryItem("jwks_uris", jwksUrisExample) - lazy val canSeeOtherAccountSwiftBicExample = ConnectorField(booleanTrue,NoDescriptionProvided) + lazy val canSeeOtherAccountSwiftBicExample = ConnectorField(booleanFalse,NoDescriptionProvided) glossaryItems += makeGlossaryItem("can_see_other_account_swift_bic", canSeeOtherAccountSwiftBicExample) lazy val staffUserIdExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) @@ -2183,7 +2183,7 @@ object ExampleValue { lazy val validFromExample = ConnectorField("2020-01-27",NoDescriptionProvided) glossaryItems += makeGlossaryItem("valid_from", validFromExample) - lazy val canDeleteImageExample = ConnectorField(booleanTrue,NoDescriptionProvided) + lazy val canDeleteImageExample = ConnectorField(booleanFalse,NoDescriptionProvided) glossaryItems += makeGlossaryItem("can_delete_image", canDeleteImageExample) lazy val toExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) @@ -2195,25 +2195,25 @@ object ExampleValue { lazy val productAttributesExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) glossaryItems += makeGlossaryItem("product_attributes", productAttributesExample) - lazy val canSeeTransactionDescriptionExample = ConnectorField(booleanTrue,NoDescriptionProvided) + lazy val canSeeTransactionDescriptionExample = ConnectorField(booleanFalse,NoDescriptionProvided) glossaryItems += makeGlossaryItem("can_see_transaction_description", canSeeTransactionDescriptionExample) lazy val faceImageExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) glossaryItems += makeGlossaryItem("face_image", faceImageExample) - lazy val canSeeBankAccountNumberExample = ConnectorField(booleanTrue,NoDescriptionProvided) + lazy val canSeeBankAccountNumberExample = ConnectorField(booleanFalse,NoDescriptionProvided) glossaryItems += makeGlossaryItem("can_see_bank_account_number", canSeeBankAccountNumberExample) lazy val glossaryItemsExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) glossaryItems += makeGlossaryItem("glossary_items", glossaryItemsExample) - lazy val isBankIdExactMatchExample = ConnectorField(booleanTrue,NoDescriptionProvided) + lazy val isBankIdExactMatchExample = ConnectorField(booleanFalse,NoDescriptionProvided) glossaryItems += makeGlossaryItem("is_bank_id_exact_match", isBankIdExactMatchExample) - lazy val isPublicExample = ConnectorField(booleanTrue,NoDescriptionProvided) + lazy val isPublicExample = ConnectorField(booleanFalse,NoDescriptionProvided) glossaryItems += makeGlossaryItem("is_public", isPublicExample) - lazy val isAccessibleExample = ConnectorField(booleanTrue,NoDescriptionProvided) + lazy val isAccessibleExample = ConnectorField(booleanFalse,NoDescriptionProvided) glossaryItems += makeGlossaryItem("ATM.is_accessible", isAccessibleExample) lazy val entitlementIdExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)