From 72ccbb055e4c4692904c8ba91bac6bde79f5154c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Mon, 27 Mar 2023 12:25:03 +0200 Subject: [PATCH 01/11] docfix/Tweak endpoint accountCurrencyCheck 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 a0f62974d..535958984 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 @@ -227,7 +227,7 @@ trait APIMethods510 { "GET", "/management/system/integrity/account-currency-check", "Check for Sensible Currencies", - s"""Check for sensible currencies at account access table. + s"""Check for sensible currencies at Bank Account model | |${authenticationRequiredMessage(true)} |""".stripMargin, From 3dca80da27d66c689f2af15eb0092c4911c8054b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Tue, 28 Mar 2023 06:54:47 +0200 Subject: [PATCH 02/11] feature/Add endpoint getCurrenciesAtBank v5.1.0 --- .../SwaggerDefinitionsJSON.scala | 4 +- .../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 | 2 + .../scala/code/bankconnectors/Connector.scala | 3 ++ .../bankconnectors/LocalMappedConnector.scala | 11 +++++ 6 files changed, 65 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 149402a25..fa2647422 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.CertificateInfoJsonV510 +import code.api.v5_1_0.{CertificateInfoJsonV510, CurrenciesJsonV510, CurrencyJsonV510} import code.endpointMapping.EndpointMappingCommons import scala.collection.immutable.List @@ -3027,6 +3027,8 @@ object SwaggerDefinitionsJSON { inverse_conversion_value = 0.998, effective_date = DateWithDayExampleObject ) + + val currenciesJsonV510 = CurrenciesJsonV510(currencies = List(CurrencyJsonV510(alphanumeric_code = "EUR"))) val counterpartyJsonV220 = CounterpartyJsonV220( name = postCounterpartyJSON.name, 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 fbe1b865b..8a78454b9 100644 --- a/obp-api/src/main/scala/code/api/util/NewStyle.scala +++ b/obp-api/src/main/scala/code/api/util/NewStyle.scala @@ -2177,7 +2177,13 @@ object NewStyle extends MdcLoggable{ } } } - + + + def getCurrentCurrencies(bankId: BankId, callContext: Option[CallContext]): OBPReturnType[List[String]] = { + Connector.connector.vend.getCurrentCurrencies(bankId, callContext) map { + i => (unboxFullOrFail(i._1, callContext, s"$InvalidConnectorResponse", 400), i._2) + } + } def getExchangeRate(bankId: BankId, fromCurrencyCode: String, toCurrencyCode: String, callContext: Option[CallContext]): Future[FXRate] = Future(Connector.connector.vend.getCurrentFxRate(bankId, fromCurrencyCode, toCurrencyCode)) map { 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 535958984..df1e5657f 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 @@ -7,7 +7,7 @@ import code.api.util.APIUtil._ import code.api.util.ApiRole._ import code.api.util.ApiTag._ import code.api.util.ErrorMessages.{$UserNotLoggedIn, BankNotFound, ConsentNotFound, InvalidJsonFormat, UnknownError, UserNotFoundByUserId, UserNotLoggedIn, _} -import code.api.util.{APIUtil, ApiRole, CurrencyUtil, NewStyle, X509} +import code.api.util.{APIUtil, ApiRole, CallContext, CurrencyUtil, NewStyle, X509} import code.api.util.NewStyle.HttpCode import code.api.v3_0_0.JSONFactory300.createAggregateMetricJson import code.api.v3_1_0.ConsentJsonV310 @@ -256,6 +256,44 @@ trait APIMethods510 { } } + + staticResourceDocs += ResourceDoc( + getCurrenciesAtBank, + implementedInApiVersion, + nameOf(getCurrenciesAtBank), + "GET", + "/banks/BANK_ID/currencies", + "Get Currencies at a Bank", + """Get Currencies specified by BANK_ID + | + """.stripMargin, + emptyObjectJson, + currenciesJsonV510, + List( + $UserNotLoggedIn, + UnknownError + ), + List(apiTagFx, apiTagNewStyle) + ) + + lazy val getCurrenciesAtBank: OBPEndpoint = { + case "banks" :: BankId(bankId) :: "currencies" :: Nil JsonGet _ => { + cc => + for { + _ <- Helper.booleanToFuture(failMsg = ConsumerHasMissingRoles + CanReadFx, cc=cc.callContext) { + checkScope(bankId.value, getConsumerPrimaryKey(cc.callContext), ApiRole.canReadFx) + } + (_, callContext) <- NewStyle.function.getBank(bankId, cc.callContext) + (currencies, callContext) <- NewStyle.function.getCurrentCurrencies(bankId, callContext) + } yield { + val json = CurrenciesJsonV510(currencies.map(CurrencyJsonV510(_))) + (json, HttpCode.`200`(callContext)) + } + + } + } + + staticResourceDocs += ResourceDoc( revokeConsentAtBank, implementedInApiVersion, 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 95d274781..937ae70c0 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 @@ -63,6 +63,8 @@ case class CheckSystemIntegrityJsonV510( success: Boolean, debug_info: Option[String] = None ) +case class CurrencyJsonV510(alphanumeric_code: String) +case class CurrenciesJsonV510(currencies: List[CurrencyJsonV510]) object JSONFactory510 { diff --git a/obp-api/src/main/scala/code/bankconnectors/Connector.scala b/obp-api/src/main/scala/code/bankconnectors/Connector.scala index 4af875ee8..2f2e7ad94 100644 --- a/obp-api/src/main/scala/code/bankconnectors/Connector.scala +++ b/obp-api/src/main/scala/code/bankconnectors/Connector.scala @@ -2,6 +2,7 @@ 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} @@ -1795,6 +1796,8 @@ trait Connector extends MdcLoggable { // def resetBadLoginAttempts(username:String):Unit + def getCurrentCurrencies(bankId: BankId, callContext: Option[CallContext]): OBPReturnType[Box[List[String]]] = Future{Failure(setUnimplementedError)} + def getCurrentFxRate(bankId: BankId, fromCurrencyCode: String, toCurrencyCode: String): Box[FXRate] = Failure(setUnimplementedError) def getCurrentFxRateCached(bankId: BankId, fromCurrencyCode: String, toCurrencyCode: String): Box[FXRate] = { /** diff --git a/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala b/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala index 9ee66be83..c32f8c8e5 100644 --- a/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala +++ b/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala @@ -2,6 +2,7 @@ package code.bankconnectors import java.util.Date import java.util.UUID.randomUUID + import _root_.akka.http.scaladsl.model.HttpMethod import code.DynamicData.DynamicDataProvider import code.DynamicEndpoint.{DynamicEndpointProvider, DynamicEndpointT} @@ -3214,6 +3215,16 @@ object LocalMappedConnector extends Connector with MdcLoggable { } } + + override def getCurrentCurrencies(bankId: BankId, callContext: Option[CallContext]): OBPReturnType[Box[List[String]]] = Future { + val rates = MappedFXRate.findAll(By(MappedFXRate.mBankId, bankId.value)) + val result = rates.map(_.fromCurrencyCode) ::: rates.map(_.toCurrencyCode) + Some(result.distinct) + } map { + (_, callContext) + } + + /** * get the latest record from FXRate table by the fields: fromCurrencyCode and toCurrencyCode. * If it is not found by (fromCurrencyCode, toCurrencyCode) order, it will try (toCurrencyCode, fromCurrencyCode) order . From 50b9abe6e6313890561bf71e9bf58f459ccba8ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Tue, 28 Mar 2023 10:54:15 +0200 Subject: [PATCH 03/11] test/Add tests for endpoint getCurrenciesAtBank v5.1.0 --- .../code/api/v5_1_0/CurrenciesTest.scala | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 obp-api/src/test/scala/code/api/v5_1_0/CurrenciesTest.scala diff --git a/obp-api/src/test/scala/code/api/v5_1_0/CurrenciesTest.scala b/obp-api/src/test/scala/code/api/v5_1_0/CurrenciesTest.scala new file mode 100644 index 000000000..acf1b48ae --- /dev/null +++ b/obp-api/src/test/scala/code/api/v5_1_0/CurrenciesTest.scala @@ -0,0 +1,64 @@ +package code.api.v5_1_0 + +import code.api.util.APIUtil.OAuth._ +import code.api.util.ApiRole +import code.api.v5_1_0.OBPAPI5_1_0.Implementations5_1_0 +import code.consumer.Consumers +import code.scope.Scope +import code.setup.DefaultUsers +import com.github.dwickern.macros.NameOf.nameOf +import com.openbankproject.commons.util.ApiVersion +import org.scalatest.Tag + +class CurrenciesTest extends V510ServerSetup with DefaultUsers { + + /** + * 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.getCurrenciesAtBank)) + + override def beforeAll(): Unit = { + super.beforeAll() + } + + override def afterAll(): Unit = { + super.afterAll() + } + + feature(s"Assuring $ApiEndpoint1 works as expected - $VersionOfApi") { + + scenario(s"We Call $ApiEndpoint1", VersionOfApi, ApiEndpoint1) { + setPropsValues("require_scopes_for_all_roles" -> "true") + val testBank = testBankId1 + val consumerId = Consumers.consumers.vend.getConsumerByConsumerKey(user1.get._1.key).map(_.id.get.toString).getOrElse("") + Scope.scope.vend.addScope(testBank.value, consumerId, ApiRole.canReadFx.toString()) + val requestGet = (v5_1_0_Request / "banks" / testBank.value / "currencies" ).GET <@ (user1) + val responseGet = makeGetRequest(requestGet) + And("We should get a 200") + responseGet.code should equal(200) + } + scenario(s"We Call $ApiEndpoint1 without a proper scope", VersionOfApi, ApiEndpoint1) { + setPropsValues("require_scopes_for_all_roles" -> "true") + val testBank = testBankId1 + val requestGet = (v5_1_0_Request / "banks" / testBank.value / "currencies" ).GET <@ (user1) + val responseGet = makeGetRequest(requestGet) + And("We should get a 403") + responseGet.code should equal(403) + } + scenario(s"We Call $ApiEndpoint1 with anonymous access", VersionOfApi, ApiEndpoint1) { + setPropsValues("require_scopes_for_all_roles" -> "true") + val testBank = testBankId1 + val requestGet = (v5_1_0_Request / "banks" / testBank.value / "currencies" ).GET + val responseGet = makeGetRequest(requestGet) + And("We should get a 401") + responseGet.code should equal(401) + } + + } + +} From 357854ae3285bf9f49d8536441d199182fe61283 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Wed, 29 Mar 2023 12:51:05 +0200 Subject: [PATCH 04/11] feature/create endpoint get currencies and tweak system integrity currency check --- .../src/main/scala/code/api/v5_1_0/APIMethods510.scala | 10 +++++----- .../main/scala/code/api/v5_1_0/JSONFactory5.1.0.scala | 4 ++-- .../scala/code/api/v5_1_0/SystemIntegrityTest.scala | 6 +++--- 3 files changed, 10 insertions(+), 10 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 df1e5657f..b032a4579 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 @@ -225,7 +225,7 @@ trait APIMethods510 { implementedInApiVersion, nameOf(accountCurrencyCheck), "GET", - "/management/system/integrity/account-currency-check", + "/management/system/integrity/banks/BANK_ID/account-currency-check", "Check for Sensible Currencies", s"""Check for sensible currencies at Bank Account model | @@ -243,15 +243,15 @@ trait APIMethods510 { ) lazy val accountCurrencyCheck: OBPEndpoint = { - case "management" :: "system" :: "integrity" :: "account-currency-check" :: Nil JsonGet _ => { + case "management" :: "system" :: "integrity" :: "banks" :: BankId(bankId) :: "account-currency-check" :: Nil JsonGet _ => { cc => for { - currenciess: List[String] <- Future { + currencies: List[String] <- Future { MappedBankAccount.findAll().map(_.accountCurrency.get).distinct } - currentCurrencies: List[String] <- Future { CurrencyUtil.getCurrencyCodes() } + (bankCurrencies, callContext) <- NewStyle.function.getCurrentCurrencies(bankId, cc.callContext) } yield { - (JSONFactory510.getSensibleCurrenciesCheck(currenciess, currentCurrencies), HttpCode.`200`(cc.callContext)) + (JSONFactory510.getSensibleCurrenciesCheck(bankCurrencies, currencies), HttpCode.`200`(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 937ae70c0..808614fb7 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 @@ -93,8 +93,8 @@ object JSONFactory510 { debug_info = debugInfo ) } - def getSensibleCurrenciesCheck(currencies: List[String], currentCurrencies: List[String]): CheckSystemIntegrityJsonV510 = { - val incorrectCurrencies: List[String] = currencies.filterNot(c => currentCurrencies.contains(c)) + def getSensibleCurrenciesCheck(bankCurrencies: List[String], accountCurrencies: List[String]): CheckSystemIntegrityJsonV510 = { + val incorrectCurrencies: List[String] = bankCurrencies.filterNot(c => accountCurrencies.contains(c)) val success = incorrectCurrencies.size == 0 val debugInfo = if(success) None else Some(s"Incorrect currencies: ${incorrectCurrencies.mkString(",")}") CheckSystemIntegrityJsonV510( diff --git a/obp-api/src/test/scala/code/api/v5_1_0/SystemIntegrityTest.scala b/obp-api/src/test/scala/code/api/v5_1_0/SystemIntegrityTest.scala index de2e41e5e..b2f645e54 100644 --- a/obp-api/src/test/scala/code/api/v5_1_0/SystemIntegrityTest.scala +++ b/obp-api/src/test/scala/code/api/v5_1_0/SystemIntegrityTest.scala @@ -134,7 +134,7 @@ class SystemIntegrityTest extends V510ServerSetup { feature(s"test $ApiEndpoint4 version $VersionOfApi - Unauthorized 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 / "management" / "system" / "integrity" / "account-currency-check").GET + val request510 = (v5_1_0_Request / "management" / "system" / "integrity" / "banks" / testBankId1.value / "account-currency-check").GET val response510 = makeGetRequest(request510) Then("We should get a 401") response510.code should equal(401) @@ -145,7 +145,7 @@ class SystemIntegrityTest extends V510ServerSetup { feature(s"test $ApiEndpoint4 version $VersionOfApi - Authorized access") { scenario("We will call the endpoint with user credentials but without a proper entitlement", ApiEndpoint1, VersionOfApi) { When("We make a request v5.1.0") - val request510 = (v5_1_0_Request / "management" / "system" / "integrity" / "account-currency-check").GET <@(user1) + val request510 = (v5_1_0_Request / "management" / "system" / "integrity" / "banks" / testBankId1.value / "account-currency-check").GET <@(user1) val response510 = makeGetRequest(request510) Then("error should be " + UserHasMissingRoles + CanGetSystemIntegrity) response510.code should equal(403) @@ -157,7 +157,7 @@ class SystemIntegrityTest extends V510ServerSetup { scenario("We will call the endpoint with user credentials and a proper entitlement", ApiEndpoint1, VersionOfApi) { Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanGetSystemIntegrity.toString) When("We make a request v5.1.0") - val request510 = (v5_1_0_Request / "management" / "system" / "integrity" / "account-currency-check").GET <@(user1) + val request510 = (v5_1_0_Request / "management" / "system" / "integrity" / "banks" / testBankId1.value / "account-currency-check").GET <@(user1) val response510 = makeGetRequest(request510) Then("We get successful response") response510.code should equal(200) From 41889520646b7ff4ba492b6456a31cda79bd10e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Thu, 30 Mar 2023 12:51:23 +0200 Subject: [PATCH 05/11] feature/Just in Time Entitlements --- .../resources/props/sample.props.template | 7 ++++ .../main/scala/code/api/util/APIUtil.scala | 37 ++++++++++++++++--- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/obp-api/src/main/resources/props/sample.props.template b/obp-api/src/main/resources/props/sample.props.template index e372d61f4..ddaf179b9 100644 --- a/obp-api/src/main/resources/props/sample.props.template +++ b/obp-api/src/main/resources/props/sample.props.template @@ -838,6 +838,13 @@ featured_apis=elasticSearchWarehouseV300 # i.e. instead of asking every user to have a Role, you can give the Role(s) to a Consumer in the form of a Scope # allow_entitlements_or_scopes=false # --------------------------------------------------------------- + +# -- Just in Time Entitlements ------------------------------- +create_just_in_time_entitlements=false +# if create_just_in_time_entitlements==true then do the following: +# If a user is trying to use a Role and the user could grant them selves the required Role(s), +# then just automatically grant the Role(s)! +# ------------------------------------------------------------- # -- Database scheduler ----------------------------- # Database scheduler interval in seconds. 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 226b0aded..d05109fb1 100644 --- a/obp-api/src/main/scala/code/api/util/APIUtil.scala +++ b/obp-api/src/main/scala/code/api/util/APIUtil.scala @@ -2213,21 +2213,48 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{ // i.e. does user has assigned at least one role from the list // when roles is empty, that means no access control, treat as pass auth check def handleEntitlementsAndScopes(bankId: String, userId: String, consumerId: String, roles: List[ApiRole]): Boolean = { - // Consumer AND User has the Role + val requireScopesForListedRoles: List[String] = getPropsValue("require_scopes_for_listed_roles", "").split(",").toList val requireScopesForRoles: immutable.Seq[String] = roles.map(_.toString()) intersect requireScopesForListedRoles + + def userHasTheRoles: Boolean = { + val userHasTheRole: Boolean = roles.isEmpty || roles.exists(hasEntitlement(bankId, userId, _)) + userHasTheRole match { + case true => userHasTheRole // Just forward + case false => + // If a user is trying to use a Role and the user could grant them selves the required Role(s), + // then just automatically grant the Role(s)! + getPropsAsBoolValue("create_just_in_time_entitlements", false) match { + case false => userHasTheRole // Just forward + case true => // Try to add missing roles + if (hasEntitlement(bankId, userId, ApiRole.canCreateEntitlementAtOneBank) || + hasEntitlement("", userId, ApiRole.canCreateEntitlementAtAnyBank)) { + // Add missing roles + roles.map { + role => + val addedEntitlement = Entitlement.entitlement.vend.addEntitlement(bankId, userId, role.toString()) + logger.info(s"Just in Time Entitlements: $addedEntitlement") + addedEntitlement + }.forall(_.isDefined) + } else { + userHasTheRole // Just forward + } + } + } + } + // Consumer AND User has the Role if(ApiPropsWithAlias.requireScopesForAllRoles || !requireScopesForRoles.isEmpty) { - roles.isEmpty || (roles.exists(hasEntitlement(bankId, userId, _)) && roles.exists(hasScope(bankId, consumerId, _))) + roles.isEmpty || (userHasTheRoles && roles.exists(hasScope(bankId, consumerId, _))) } // Consumer OR User has the Role else if(getPropsAsBoolValue("allow_entitlements_or_scopes", false)) { - roles.isEmpty || - roles.exists(hasEntitlement(bankId, userId, _)) || + roles.isEmpty || + userHasTheRoles || roles.exists(role => hasScope(if (role.requiresBankId) bankId else "", consumerId, role)) } // User has the Role else { - roles.isEmpty || roles.exists(hasEntitlement(bankId, userId, _)) + userHasTheRoles } } From dc52d46c3fbe6aa60282b6289151a913a31157ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Thu, 30 Mar 2023 15:44:16 +0200 Subject: [PATCH 06/11] Revert "feature/Just in Time Entitlements" This reverts commit 41889520 --- .../resources/props/sample.props.template | 7 ---- .../main/scala/code/api/util/APIUtil.scala | 37 +++---------------- 2 files changed, 5 insertions(+), 39 deletions(-) diff --git a/obp-api/src/main/resources/props/sample.props.template b/obp-api/src/main/resources/props/sample.props.template index ddaf179b9..e372d61f4 100644 --- a/obp-api/src/main/resources/props/sample.props.template +++ b/obp-api/src/main/resources/props/sample.props.template @@ -838,13 +838,6 @@ featured_apis=elasticSearchWarehouseV300 # i.e. instead of asking every user to have a Role, you can give the Role(s) to a Consumer in the form of a Scope # allow_entitlements_or_scopes=false # --------------------------------------------------------------- - -# -- Just in Time Entitlements ------------------------------- -create_just_in_time_entitlements=false -# if create_just_in_time_entitlements==true then do the following: -# If a user is trying to use a Role and the user could grant them selves the required Role(s), -# then just automatically grant the Role(s)! -# ------------------------------------------------------------- # -- Database scheduler ----------------------------- # Database scheduler interval in seconds. 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 d05109fb1..226b0aded 100644 --- a/obp-api/src/main/scala/code/api/util/APIUtil.scala +++ b/obp-api/src/main/scala/code/api/util/APIUtil.scala @@ -2213,48 +2213,21 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{ // i.e. does user has assigned at least one role from the list // when roles is empty, that means no access control, treat as pass auth check def handleEntitlementsAndScopes(bankId: String, userId: String, consumerId: String, roles: List[ApiRole]): Boolean = { - + // Consumer AND User has the Role val requireScopesForListedRoles: List[String] = getPropsValue("require_scopes_for_listed_roles", "").split(",").toList val requireScopesForRoles: immutable.Seq[String] = roles.map(_.toString()) intersect requireScopesForListedRoles - - def userHasTheRoles: Boolean = { - val userHasTheRole: Boolean = roles.isEmpty || roles.exists(hasEntitlement(bankId, userId, _)) - userHasTheRole match { - case true => userHasTheRole // Just forward - case false => - // If a user is trying to use a Role and the user could grant them selves the required Role(s), - // then just automatically grant the Role(s)! - getPropsAsBoolValue("create_just_in_time_entitlements", false) match { - case false => userHasTheRole // Just forward - case true => // Try to add missing roles - if (hasEntitlement(bankId, userId, ApiRole.canCreateEntitlementAtOneBank) || - hasEntitlement("", userId, ApiRole.canCreateEntitlementAtAnyBank)) { - // Add missing roles - roles.map { - role => - val addedEntitlement = Entitlement.entitlement.vend.addEntitlement(bankId, userId, role.toString()) - logger.info(s"Just in Time Entitlements: $addedEntitlement") - addedEntitlement - }.forall(_.isDefined) - } else { - userHasTheRole // Just forward - } - } - } - } - // Consumer AND User has the Role if(ApiPropsWithAlias.requireScopesForAllRoles || !requireScopesForRoles.isEmpty) { - roles.isEmpty || (userHasTheRoles && roles.exists(hasScope(bankId, consumerId, _))) + roles.isEmpty || (roles.exists(hasEntitlement(bankId, userId, _)) && roles.exists(hasScope(bankId, consumerId, _))) } // Consumer OR User has the Role else if(getPropsAsBoolValue("allow_entitlements_or_scopes", false)) { - roles.isEmpty || - userHasTheRoles || + roles.isEmpty || + roles.exists(hasEntitlement(bankId, userId, _)) || roles.exists(role => hasScope(if (role.requiresBankId) bankId else "", consumerId, role)) } // User has the Role else { - userHasTheRoles + roles.isEmpty || roles.exists(hasEntitlement(bankId, userId, _)) } } From 2b8d2c8d948bbb64e95cfce2807c5d46284102b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Fri, 31 Mar 2023 07:11:54 +0200 Subject: [PATCH 07/11] feature/Create ATM Attributes Endpoints --- .../main/scala/bootstrap/liftweb/Boot.scala | 2 + .../SwaggerDefinitionsJSON.scala | 17 +- .../main/scala/code/api/util/ApiRole.scala | 18 +- .../main/scala/code/api/util/NewStyle.scala | 59 +++++ .../scala/code/api/v5_1_0/APIMethods510.scala | 239 +++++++++++++++++- .../code/api/v5_1_0/JSONFactory5.1.0.scala | 62 +++++ .../code/atmattribute/AtmAttribute.scala | 47 ++++ .../MappedAtmAttributeProvider.scala | 105 ++++++++ .../scala/code/bankconnectors/Connector.scala | 22 ++ .../bankconnectors/LocalMappedConnector.scala | 35 +++ .../commons/model/CommonModelTrait.scala | 10 + .../commons/model/enums/Enumerations.scala | 8 +- 12 files changed, 618 insertions(+), 6 deletions(-) create mode 100644 obp-api/src/main/scala/code/atmattribute/AtmAttribute.scala create mode 100644 obp-api/src/main/scala/code/atmattribute/MappedAtmAttributeProvider.scala diff --git a/obp-api/src/main/scala/bootstrap/liftweb/Boot.scala b/obp-api/src/main/scala/bootstrap/liftweb/Boot.scala index 4e38a3d4e..1cd2b68b9 100644 --- a/obp-api/src/main/scala/bootstrap/liftweb/Boot.scala +++ b/obp-api/src/main/scala/bootstrap/liftweb/Boot.scala @@ -50,6 +50,7 @@ import code.api.util.migration.Migration import code.api.util.migration.Migration.DbFunction import code.apicollection.ApiCollection import code.apicollectionendpoint.ApiCollectionEndpoint +import code.atmattribute.AtmAttribute import code.atms.MappedAtm import code.authtypevalidation.AuthenticationTypeValidation import code.bankattribute.BankAttribute @@ -1010,6 +1011,7 @@ object ToSchemify { // The following tables are accessed directly via Mapper / JDBC val models: List[MetaMapper[_]] = List( AuthUser, + AtmAttribute, Admin, MappedBank, MappedBankAccount, 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 df7bb2264..d7ed89ad4 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.{CertificateInfoJsonV510, CurrenciesJsonV510, CurrencyJsonV510} +import code.api.v5_1_0.{AtmAttributeJsonV510, AtmAttributeResponseJsonV510, CertificateInfoJsonV510, CurrenciesJsonV510, CurrencyJsonV510} import code.endpointMapping.EndpointMappingCommons import scala.collection.immutable.List @@ -3999,6 +3999,12 @@ object SwaggerDefinitionsJSON { value = "12345678", is_active = Some(true) ) + val atmAttributeJsonV510 = AtmAttributeJsonV510( + name = "TAX_ID", + `type` = "INTEGER", + value = "12345678", + is_active = Some(true) + ) val bankAttributeResponseJsonV400 = BankAttributeResponseJsonV400( bank_id = bankIdExample.value, bank_attribute_id = "613c83ea-80f9-4560-8404-b9cd4ec42a7f", @@ -4007,6 +4013,15 @@ object SwaggerDefinitionsJSON { value = "2012-04-23", is_active = Some(true) ) + val atmAttributeResponseJsonV510 = AtmAttributeResponseJsonV510( + bank_id = bankIdExample.value, + atm_id = atmIdExample.value, + atm_attribute_id = "613c83ea-80f9-4560-8404-b9cd4ec42a7f", + name = "OVERDRAFT_START_DATE", + `type` = "DATE_WITH_DAY", + value = "2012-04-23", + is_active = Some(true) + ) val accountAttributeJson = AccountAttributeJson( diff --git a/obp-api/src/main/scala/code/api/util/ApiRole.scala b/obp-api/src/main/scala/code/api/util/ApiRole.scala index 64fe185fd..a61adeec9 100644 --- a/obp-api/src/main/scala/code/api/util/ApiRole.scala +++ b/obp-api/src/main/scala/code/api/util/ApiRole.scala @@ -474,10 +474,16 @@ object ApiRole { lazy val canUpdateProductAttribute = CanUpdateProductAttribute() case class CanUpdateBankAttribute(requiresBankId: Boolean = true) extends ApiRole - lazy val canUpdateBankAttribute = CanUpdateBankAttribute() + lazy val canUpdateBankAttribute = CanUpdateBankAttribute() + + case class CanUpdateAtmAttribute(requiresBankId: Boolean = true) extends ApiRole + lazy val canUpdateAtmAttribute = CanUpdateAtmAttribute() case class CanGetBankAttribute(requiresBankId: Boolean = true) extends ApiRole - lazy val canGetBankAttribute = CanGetBankAttribute() + lazy val canGetBankAttribute = CanGetBankAttribute() + + case class CanGetAtmAttribute(requiresBankId: Boolean = true) extends ApiRole + lazy val canGetAtmAttribute = CanGetAtmAttribute() case class CanGetProductAttribute(requiresBankId: Boolean = true) extends ApiRole lazy val canGetProductAttribute = CanGetProductAttribute() @@ -486,13 +492,19 @@ object ApiRole { lazy val canDeleteProductAttribute = CanDeleteProductAttribute() case class CanDeleteBankAttribute(requiresBankId: Boolean = true) extends ApiRole - lazy val canDeleteBankAttribute = CanDeleteBankAttribute() + lazy val canDeleteBankAttribute = CanDeleteBankAttribute() + + case class CanDeleteAtmAttribute(requiresBankId: Boolean = true) extends ApiRole + lazy val canDeleteAtmAttribute = CanDeleteAtmAttribute() case class CanCreateProductAttribute(requiresBankId: Boolean = true) extends ApiRole lazy val canCreateProductAttribute = CanCreateProductAttribute() case class CanCreateBankAttribute(requiresBankId: Boolean = true) extends ApiRole lazy val canCreateBankAttribute = CanCreateBankAttribute() + + case class CanCreateAtmAttribute(requiresBankId: Boolean = true) extends ApiRole + lazy val canCreateAtmAttribute = CanCreateAtmAttribute() case class CanUpdateProductFee(requiresBankId: Boolean = true) extends ApiRole lazy val canUpdateProductFee = CanUpdateProductFee() 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 8a78454b9..0ea36cbd9 100644 --- a/obp-api/src/main/scala/code/api/util/NewStyle.scala +++ b/obp-api/src/main/scala/code/api/util/NewStyle.scala @@ -3,6 +3,7 @@ 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} @@ -67,6 +68,7 @@ import code.api.dynamic.endpoint.helper.DynamicEndpointHelper import code.api.v4_0_0.JSONFactory400 import code.api.dynamic.endpoint.helper.DynamicEndpointHelper import code.api.dynamic.entity.helper.{DynamicEntityHelper, DynamicEntityInfo} +import code.atmattribute.AtmAttribute import code.bankattribute.BankAttribute import code.connectormethod.{ConnectorMethodProvider, JsonConnectorMethod} import code.customeraccountlinks.CustomerAccountLinkTrait @@ -1660,6 +1662,30 @@ object NewStyle extends MdcLoggable{ i => (connectorEmptyResponse(i._1, callContext), i._2) } } + + def createOrUpdateAtmAttribute( + bankId: BankId, + atmId: AtmId, + atmAttributeId: Option[String], + name: String, + attributeType: AtmAttributeType.Value, + value: String, + isActive: Option[Boolean], + callContext: Option[CallContext] + ): OBPReturnType[AtmAttribute] = { + Connector.connector.vend.createOrUpdateAtmAttribute( + bankId: BankId, + atmId: AtmId, + atmAttributeId: Option[String], + name: String, + attributeType: AtmAttributeType.Value, + value: String, + isActive: Option[Boolean], + callContext: Option[CallContext] + ) map { + i => (connectorEmptyResponse(i._1, callContext), i._2) + } + } def getBankAttributesByBank(bank: BankId,callContext: Option[CallContext]): OBPReturnType[List[BankAttribute]] = { Connector.connector.vend.getBankAttributesByBank( @@ -1669,6 +1695,15 @@ object NewStyle extends MdcLoggable{ i => (connectorEmptyResponse(i._1, callContext), i._2) } } + def getAtmAttributesByAtm(bank: BankId, atm: AtmId, callContext: Option[CallContext]): OBPReturnType[List[AtmAttribute]] = { + Connector.connector.vend.getAtmAttributesByAtm( + bank: BankId, + atm: AtmId, + callContext: Option[CallContext] + ) map { + i => (connectorEmptyResponse(i._1, callContext), i._2) + } + } def getProductAttributesByBankAndCode( bank: BankId, productCode: ProductCode, @@ -1707,6 +1742,18 @@ object NewStyle extends MdcLoggable{ } } + def getAtmAttributeById( + atmAttributeId: String, + callContext: Option[CallContext] + ): OBPReturnType[AtmAttribute] = { + Connector.connector.vend.getAtmAttributeById( + atmAttributeId: String, + callContext: Option[CallContext] + ) map { + i => (connectorEmptyResponse(i._1, callContext), i._2) + } + } + def deleteBankAttribute( bankAttributeId: String, callContext: Option[CallContext] @@ -1717,6 +1764,18 @@ object NewStyle extends MdcLoggable{ ) map { i => (connectorEmptyResponse(i._1, callContext), i._2) } + } + + def deleteAtmAttribute( + atmAttributeId: String, + callContext: Option[CallContext] + ): OBPReturnType[Boolean] = { + Connector.connector.vend.deleteAtmAttribute( + atmAttributeId: String, + callContext: Option[CallContext] + ) map { + i => (connectorEmptyResponse(i._1, callContext), i._2) + } } def deleteProductAttribute( 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 b032a4579..a00a9b0cc 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 @@ -24,7 +24,8 @@ import code.util.Helper import code.views.system.{AccountAccess, ViewDefinition} import com.github.dwickern.macros.NameOf.nameOf import com.openbankproject.commons.ExecutionContext.Implicits.global -import com.openbankproject.commons.model.BankId +import com.openbankproject.commons.model.{AtmId, BankId} +import com.openbankproject.commons.model.enums.AtmAttributeType import com.openbankproject.commons.util.{ApiVersion, ScannedApiVersion} import net.liftweb.common.Full import net.liftweb.http.rest.RestHelper @@ -292,6 +293,242 @@ trait APIMethods510 { } } + + + + + + + + + + + + staticResourceDocs += ResourceDoc( + createAtmAttribute, + implementedInApiVersion, + nameOf(createAtmAttribute), + "POST", + "/banks/BANK_ID/atms/ATM_ID/attributes", + "Create ATM Attribute", + s""" Create ATM Attribute + | + |The type field must be one of "STRING", "INTEGER", "DOUBLE" or DATE_WITH_DAY" + | + |${authenticationRequiredMessage(true)} + | + |""", + atmAttributeJsonV510, + atmAttributeResponseJsonV510, + List( + $UserNotLoggedIn, + $BankNotFound, + InvalidJsonFormat, + UnknownError + ), + List(apiTagATM, apiTagNewStyle), + Some(List(canCreateAtmAttribute)) + ) + + lazy val createAtmAttribute : OBPEndpoint = { + case "banks" :: BankId(bankId) :: "atms" :: AtmId(atmId) :: "attributes" :: Nil JsonPost json -> _=> { + cc => + for { + (_, callContext) <- NewStyle.function.getAtm(bankId, atmId, cc.callContext) + failMsg = s"$InvalidJsonFormat The Json body should be the $AtmAttributeJsonV510 " + postedData <- NewStyle.function.tryons(failMsg, 400, callContext) { + json.extract[AtmAttributeJsonV510] + } + failMsg = s"$InvalidJsonFormat The `Type` field can only accept the following field: " + + s"${AtmAttributeType.DOUBLE}(12.1234), ${AtmAttributeType.STRING}(TAX_NUMBER), ${AtmAttributeType.INTEGER}(123) and ${AtmAttributeType.DATE_WITH_DAY}(2012-04-23)" + bankAttributeType <- NewStyle.function.tryons(failMsg, 400, callContext) { + AtmAttributeType.withName(postedData.`type`) + } + (atmAttribute, callContext) <- NewStyle.function.createOrUpdateAtmAttribute( + bankId, + atmId, + None, + postedData.name, + bankAttributeType, + postedData.value, + postedData.is_active, + callContext: Option[CallContext] + ) + } yield { + (JSONFactory510.createAtmAttributeJson(atmAttribute), HttpCode.`201`(callContext)) + } + } + } + + staticResourceDocs += ResourceDoc( + getAtmAttributes, + implementedInApiVersion, + nameOf(getAtmAttributes), + "GET", + "/banks/BANK_ID/atms/ATM_ID/attributes", + "Get ATM Attributes", + s""" Get ATM Attributes + | + |${authenticationRequiredMessage(true)} + | + |""", + EmptyBody, + transactionAttributesResponseJson, + List( + $UserNotLoggedIn, + $BankNotFound, + InvalidJsonFormat, + UnknownError + ), + List(apiTagATM, apiTagNewStyle), + Some(List(canGetAtmAttribute)) + ) + + lazy val getAtmAttributes : OBPEndpoint = { + case "banks" :: BankId(bankId) :: "atms" :: AtmId(atmId) :: "attributes" :: Nil JsonGet _ => { + cc => + for { + (_, callContext) <- NewStyle.function.getAtm(bankId, atmId, cc.callContext) + (attributes, callContext) <- NewStyle.function.getAtmAttributesByAtm(bankId, atmId, callContext) + } yield { + (JSONFactory510.createAtmAttributesJson(attributes), HttpCode.`200`(callContext)) + } + } + } + + staticResourceDocs += ResourceDoc( + getAtmAttribute, + implementedInApiVersion, + nameOf(getAtmAttribute), + "GET", + "/banks/BANK_ID/atms/ATM_ID/attributes/ATM_ATTRIBUTE_ID", + "Get ATM Attribute By ATM_ATTRIBUTE_ID", + s""" Get ATM Attribute By ATM_ATTRIBUTE_ID + | + |${authenticationRequiredMessage(true)} + | + |""", + EmptyBody, + atmAttributeResponseJsonV510, + List( + $UserNotLoggedIn, + $BankNotFound, + InvalidJsonFormat, + UnknownError + ), + List(apiTagATM, apiTagNewStyle), + Some(List(canGetAtmAttribute)) + ) + + lazy val getAtmAttribute : OBPEndpoint = { + case "banks" :: BankId(bankId) :: "atms" :: AtmId(atmId) :: "attributes" :: atmAttributeId :: Nil JsonGet _ => { + cc => + for { + (_, callContext) <- NewStyle.function.getAtm(bankId, atmId, cc.callContext) + (attribute, callContext) <- NewStyle.function.getAtmAttributeById(atmAttributeId, callContext) + } yield { + (JSONFactory510.createAtmAttributeJson(attribute), HttpCode.`200`(callContext)) + } + } + } + + + staticResourceDocs += ResourceDoc( + updateAtmAttribute, + implementedInApiVersion, + nameOf(updateAtmAttribute), + "PUT", + "/banks/BANK_ID/atms/ATM_ID/attributes/ATM_ATTRIBUTE_ID", + "Update ATM Attribute", + s""" Update ATM Attribute. + | + |Update an ATM Attribute by its id. + | + |${authenticationRequiredMessage(true)} + | + |""", + atmAttributeJsonV510, + atmAttributeResponseJsonV510, + List( + $UserNotLoggedIn, + $BankNotFound, + UserHasMissingRoles, + UnknownError + ), + List(apiTagATM, apiTagNewStyle), + Some(List(canUpdateAtmAttribute)) + ) + + lazy val updateAtmAttribute : OBPEndpoint = { + case "banks" :: BankId(bankId) :: "atms" :: AtmId(atmId) :: "attributes" :: atmAttributeId :: Nil JsonPut json -> _ =>{ + cc => + for { + (_, callContext) <- NewStyle.function.getAtm(bankId, atmId, cc.callContext) + failMsg = s"$InvalidJsonFormat The Json body should be the $AtmAttributeJsonV510 " + postedData <- NewStyle.function.tryons(failMsg, 400, callContext) { + json.extract[AtmAttributeJsonV510] + } + failMsg = s"$InvalidJsonFormat The `Type` field can only accept the following field: " + + s"${AtmAttributeType.DOUBLE}(12.1234), ${AtmAttributeType.STRING}(TAX_NUMBER), ${AtmAttributeType.INTEGER}(123) and ${AtmAttributeType.DATE_WITH_DAY}(2012-04-23)" + atmAttributeType <- NewStyle.function.tryons(failMsg, 400, cc.callContext) { + AtmAttributeType.withName(postedData.`type`) + } + (_, callContext) <- NewStyle.function.getAtmAttributeById(atmAttributeId, cc.callContext) + (atmAttribute, callContext) <- NewStyle.function.createOrUpdateAtmAttribute( + bankId, + atmId, + Some(atmAttributeId), + postedData.name, + atmAttributeType, + postedData.value, + postedData.is_active, + callContext: Option[CallContext] + ) + } yield { + (JSONFactory510.createAtmAttributeJson(atmAttribute), HttpCode.`200`(callContext)) + } + } + } + + + staticResourceDocs += ResourceDoc( + deleteAtmAttribute, + implementedInApiVersion, + nameOf(deleteAtmAttribute), + "DELETE", + "/banks/BANK_ID/atms/ATM_ID/attributes/ATM_ATTRIBUTE_ID", + "Delete ATM Attribute", + s""" Delete ATM Attribute + | + |Delete a Atm Attribute by its id. + | + |${authenticationRequiredMessage(true)} + | + |""", + EmptyBody, + EmptyBody, + List( + $UserNotLoggedIn, + $BankNotFound, + UserHasMissingRoles, + UnknownError + ), + List(apiTagATM, apiTagNewStyle), + Some(List(canDeleteAtmAttribute)) + ) + + lazy val deleteAtmAttribute : OBPEndpoint = { + case "banks" :: BankId(bankId) :: "atms" :: AtmId(atmId) :: "attributes" :: atmAttributeId :: Nil JsonDelete _=> { + cc => + for { + (_, callContext) <- NewStyle.function.getAtm(bankId, atmId, cc.callContext) + (atmAttribute, callContext) <- NewStyle.function.deleteAtmAttribute(atmAttributeId, callContext) + } yield { + (Full(atmAttribute), HttpCode.`204`(callContext)) + } + } + } + staticResourceDocs += ResourceDoc( 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 808614fb7..c111eceb8 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 @@ -30,6 +30,7 @@ import code.api.Constant import code.api.util.APIUtil import code.api.util.APIUtil.gitCommit import code.api.v4_0_0.{EnergySource400, HostedAt400, HostedBy400} +import code.atmattribute.AtmAttribute import code.views.system.{AccountAccess, ViewDefinition} import com.openbankproject.commons.util.{ApiVersion, ScannedApiVersion} @@ -67,6 +68,53 @@ case class CurrencyJsonV510(alphanumeric_code: String) case class CurrenciesJsonV510(currencies: List[CurrencyJsonV510]) + +case class ProductAttributeJsonV510( + name: String, + `type`: String, + value: String, + is_active: Option[Boolean] + ) +case class ProductAttributeResponseJsonV510( + bank_id: String, + product_code: String, + product_attribute_id: String, + name: String, + `type`: String, + value: String, + is_active: Option[Boolean] + ) +case class ProductAttributeResponseWithoutBankIdJsonV510( + product_code: String, + product_attribute_id: String, + name: String, + `type`: String, + value: String, + is_active: Option[Boolean] + ) + +case class AtmAttributeJsonV510( + name: String, + `type`: String, + value: String, + is_active: Option[Boolean]) + +case class AtmAttributeResponseJsonV510( + bank_id: String, + atm_id: String, + atm_attribute_id: String, + name: String, + `type`: String, + value: String, + is_active: Option[Boolean] + ) +case class AtmAttributesResponseJsonV510(bank_attributes: List[AtmAttributeResponseJsonV510]) +case class AtmAttributeBankResponseJsonV510(name: String, + value: String) +case class AtmAttributesResponseJson(list: List[AtmAttributeBankResponseJsonV510]) + + + object JSONFactory510 { def getCustomViewNamesCheck(views: List[ViewDefinition]): CheckSystemIntegrityJsonV510 = { @@ -136,6 +184,20 @@ object JSONFactory510 { ) } + def createAtmAttributeJson(atmAttribute: AtmAttribute): AtmAttributeResponseJsonV510 = + AtmAttributeResponseJsonV510( + bank_id = atmAttribute.bankId.value, + atm_id = atmAttribute.atmId.value, + atm_attribute_id = atmAttribute.atmAttributeId, + name = atmAttribute.name, + `type` = atmAttribute.attributeType.toString, + value = atmAttribute.value, + is_active = atmAttribute.isActive + ) + + def createAtmAttributesJson(bankAttributes: List[AtmAttribute]): AtmAttributesResponseJsonV510 = + AtmAttributesResponseJsonV510(bankAttributes.map(createAtmAttributeJson)) + } diff --git a/obp-api/src/main/scala/code/atmattribute/AtmAttribute.scala b/obp-api/src/main/scala/code/atmattribute/AtmAttribute.scala new file mode 100644 index 000000000..fd991db67 --- /dev/null +++ b/obp-api/src/main/scala/code/atmattribute/AtmAttribute.scala @@ -0,0 +1,47 @@ +package code.atmattribute + +/* For ProductAttribute */ + +import com.openbankproject.commons.model.{AtmId, BankId} +import com.openbankproject.commons.model.enums.AtmAttributeType +import net.liftweb.common.{Box, Logger} +import net.liftweb.util.SimpleInjector + +import scala.concurrent.Future + +object AtmAttributeX extends SimpleInjector { + + val atmAttributeProvider = new Inject(buildOne _) {} + + def buildOne: AtmAttributeProviderTrait = AtmAttributeProvider + + // Helper to get the count out of an option + def countOfAtmAttribute(listOpt: Option[List[AtmAttribute]]): Int = { + val count = listOpt match { + case Some(list) => list.size + case None => 0 + } + count + } + + +} + +trait AtmAttributeProviderTrait { + + private val logger = Logger(classOf[AtmAttributeProviderTrait]) + + def getAtmAttributesFromProvider(bankId: BankId, atmId: AtmId): Future[Box[List[AtmAttribute]]] + + def getAtmAttributeById(AtmAttributeId: String): Future[Box[AtmAttribute]] + + def createOrUpdateAtmAttribute(bankId : BankId, + atmId: AtmId, + AtmAttributeId: Option[String], + name: String, + attributeType: AtmAttributeType.Value, + value: String, + isActive: Option[Boolean]): Future[Box[AtmAttribute]] + def deleteAtmAttribute(AtmAttributeId: String): Future[Box[Boolean]] + // End of Trait +} diff --git a/obp-api/src/main/scala/code/atmattribute/MappedAtmAttributeProvider.scala b/obp-api/src/main/scala/code/atmattribute/MappedAtmAttributeProvider.scala new file mode 100644 index 000000000..11080f46d --- /dev/null +++ b/obp-api/src/main/scala/code/atmattribute/MappedAtmAttributeProvider.scala @@ -0,0 +1,105 @@ +package code.atmattribute + +import code.util.{MappedUUID, UUIDString} +import com.openbankproject.commons.ExecutionContext.Implicits.global +import com.openbankproject.commons.model.enums.AtmAttributeType +import com.openbankproject.commons.model.{AtmAttributeTrait, AtmId, BankId} +import net.liftweb.common.{Box, Empty, Full} +import net.liftweb.mapper.{MappedBoolean, _} +import net.liftweb.util.Helpers.tryo + +import scala.concurrent.Future + + +object AtmAttributeProvider extends AtmAttributeProviderTrait { + + override def getAtmAttributesFromProvider(bankId: BankId, atmId: AtmId): Future[Box[List[AtmAttribute]]] = + Future { + Box !! AtmAttribute.findAll( + By(AtmAttribute.BankId_, bankId.value), + By(AtmAttribute.AtmId_, atmId.value) + ) + } + + override def getAtmAttributeById(AtmAttributeId: String): Future[Box[AtmAttribute]] = Future { + AtmAttribute.find(By(AtmAttribute.AtmAttributeId, AtmAttributeId)) + } + + override def createOrUpdateAtmAttribute(bankId: BankId, + atmId: AtmId, + AtmAttributeId: Option[String], + name: String, + attributeType: AtmAttributeType.Value, + value: String, + isActive: Option[Boolean]): Future[Box[AtmAttribute]] = { + AtmAttributeId match { + case Some(id) => Future { + AtmAttribute.find(By(AtmAttribute.AtmAttributeId, id)) match { + case Full(attribute) => tryo { + attribute + .BankId_(bankId.value) + .AtmId_(atmId.value) + .Name(name) + .Type(attributeType.toString) + .`Value`(value) + .IsActive(isActive.getOrElse(true)) + .saveMe() + } + case _ => Empty + } + } + case None => Future { + Full { + AtmAttribute.create + .BankId_(bankId.value) + .AtmId_(atmId.value) + .Name(name) + .Type(attributeType.toString()) + .`Value`(value) + .IsActive(isActive.getOrElse(true)) + .saveMe() + } + } + } + } + + override def deleteAtmAttribute(AtmAttributeId: String): Future[Box[Boolean]] = Future { + Some( + AtmAttribute.bulkDelete_!!(By(AtmAttribute.AtmAttributeId, AtmAttributeId)) + ) + } +} + +class AtmAttribute extends AtmAttributeTrait with LongKeyedMapper[AtmAttribute] with IdPK { + + override def getSingleton = AtmAttribute + + object BankId_ extends UUIDString(this) { + override def dbColumnName = "BankId" + } + object AtmId_ extends UUIDString(this) { + override def dbColumnName = "AtmId" + } + object AtmAttributeId extends MappedUUID(this) + object Name extends MappedString(this, 50) + object Type extends MappedString(this, 50) + object `Value` extends MappedString(this, 255) + object IsActive extends MappedBoolean(this) { + override def defaultValue = true + } + + + override def bankId: BankId = BankId(BankId_.get) + override def atmId: AtmId = AtmId(AtmId_.get) + override def atmAttributeId: String = AtmAttributeId.get + override def name: String = Name.get + override def attributeType: AtmAttributeType.Value = AtmAttributeType.withName(Type.get) + override def value: String = `Value`.get + override def isActive: Option[Boolean] = if (IsActive.jdbcFriendly(IsActive.calcFieldName) == null) { None } else Some(IsActive.get) + +} + +object AtmAttribute extends AtmAttribute with LongKeyedMetaMapper[AtmAttribute] { + override def dbIndexes: List[BaseIndex[AtmAttribute]] = Index(BankId_, AtmId_) :: super.dbIndexes +} + diff --git a/obp-api/src/main/scala/code/bankconnectors/Connector.scala b/obp-api/src/main/scala/code/bankconnectors/Connector.scala index 2f2e7ad94..e2ad37791 100644 --- a/obp-api/src/main/scala/code/bankconnectors/Connector.scala +++ b/obp-api/src/main/scala/code/bankconnectors/Connector.scala @@ -16,6 +16,7 @@ import code.api.v1_4_0.JSONFactory1_4_0.TransactionRequestAccountJsonV140 import code.api.v2_1_0._ import code.api.v4_0_0.ModeratedFirehoseAccountsJsonV400 import code.api.{APIFailure, APIFailureNewStyle} +import code.atmattribute.AtmAttribute import code.bankattribute.BankAttribute import code.bankconnectors.LocalMappedConnector.setUnimplementedError import code.bankconnectors.akka.AkkaConnector_vDec2018 @@ -2122,13 +2123,30 @@ trait Connector extends MdcLoggable { isActive: Option[Boolean], callContext: Option[CallContext] ): OBPReturnType[Box[BankAttribute]] = Future{(Failure(setUnimplementedError), callContext)} + + def createOrUpdateAtmAttribute(bankId: BankId, + atmId: AtmId, + atmAttributeId: Option[String], + name: String, + atmAttributeType: AtmAttributeType.Value, + value: String, + isActive: Option[Boolean], + callContext: Option[CallContext] + ): OBPReturnType[Box[AtmAttribute]] = Future{(Failure(setUnimplementedError), callContext)} def getBankAttributesByBank(bank: BankId, callContext: Option[CallContext]): OBPReturnType[Box[List[BankAttribute]]] = Future{(Failure(setUnimplementedError), callContext)} + def getAtmAttributesByAtm(bank: BankId, atm: AtmId, callContext: Option[CallContext]): OBPReturnType[Box[List[AtmAttribute]]] = + Future{(Failure(setUnimplementedError), callContext)} + def getBankAttributeById(bankAttributeId: String, callContext: Option[CallContext] ): OBPReturnType[Box[BankAttribute]] = Future{(Failure(setUnimplementedError), callContext)} + + def getAtmAttributeById(atmAttributeId: String, + callContext: Option[CallContext]): OBPReturnType[Box[AtmAttribute]] = + Future{(Failure(setUnimplementedError), callContext)} def getProductAttributeById( productAttributeId: String, @@ -2146,6 +2164,10 @@ trait Connector extends MdcLoggable { callContext: Option[CallContext] ): OBPReturnType[Box[Boolean]] = Future{(Failure(setUnimplementedError), callContext)} + def deleteAtmAttribute(atmAttributeId: String, + callContext: Option[CallContext] + ): OBPReturnType[Box[Boolean]] = Future{(Failure(setUnimplementedError), callContext)} + def deleteProductAttribute( productAttributeId: String, callContext: Option[CallContext] diff --git a/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala b/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala index c32f8c8e5..1bf2d3759 100644 --- a/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala +++ b/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala @@ -22,6 +22,7 @@ import code.api.util._ import code.api.v1_4_0.JSONFactory1_4_0.TransactionRequestAccountJsonV140 import code.api.v2_1_0._ import code.api.v4_0_0.{PostSimpleCounterpartyJson400, TransactionRequestBodySimpleJsonV400} +import code.atmattribute.{AtmAttribute, AtmAttributeX} import code.atms.Atms.Atm import code.atms.{Atms, MappedAtm} import code.bankattribute.{BankAttribute, BankAttributeX} @@ -3833,12 +3834,36 @@ object LocalMappedConnector extends Connector with MdcLoggable { value: String, isActive: Option[Boolean]) map { (_, callContext) } + + override def createOrUpdateAtmAttribute(bankId: BankId, + atmId: AtmId, + atmAttributeId: Option[String], + name: String, + atmAttributeType: AtmAttributeType.Value, + value: String, + isActive: Option[Boolean], + callContext: Option[CallContext] + ): OBPReturnType[Box[AtmAttribute]] = + AtmAttributeX.atmAttributeProvider.vend.createOrUpdateAtmAttribute( + bankId: BankId, + atmId: AtmId, + atmAttributeId: Option[String], + name: String, + atmAttributeType: AtmAttributeType.Value, + value: String, isActive: Option[Boolean]) map { + (_, callContext) + } override def getBankAttributesByBank(bank: BankId, callContext: Option[CallContext]): OBPReturnType[Box[List[BankAttribute]]] = BankAttributeX.bankAttributeProvider.vend.getBankAttributesFromProvider(bank: BankId) map { (_, callContext) } + + override def getAtmAttributesByAtm(bank: BankId, atm: AtmId, callContext: Option[CallContext]): OBPReturnType[Box[List[AtmAttribute]]] = + AtmAttributeX.atmAttributeProvider.vend.getAtmAttributesFromProvider(bank: BankId, atm: AtmId) map { + (_, callContext) + } override def getProductAttributesByBankAndCode( bank: BankId, @@ -3854,6 +3879,11 @@ object LocalMappedConnector extends Connector with MdcLoggable { (_, callContext) } + override def getAtmAttributeById(atmAttributeId: String, callContext: Option[CallContext]): OBPReturnType[Box[AtmAttribute]] = + AtmAttributeX.atmAttributeProvider.vend.getAtmAttributeById(atmAttributeId: String) map { + (_, callContext) + } + override def getProductAttributeById( productAttributeId: String, callContext: Option[CallContext] @@ -3867,6 +3897,11 @@ object LocalMappedConnector extends Connector with MdcLoggable { BankAttributeX.bankAttributeProvider.vend.deleteBankAttribute(bankAttributeId: String) map { (_, callContext) } + override def deleteAtmAttribute(atmAttributeId: String, + callContext: Option[CallContext]): OBPReturnType[Box[Boolean]] = + AtmAttributeX.atmAttributeProvider.vend.deleteAtmAttribute(atmAttributeId: String) map { + (_, callContext) + } override def deleteProductAttribute( productAttributeId: String, diff --git a/obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModelTrait.scala b/obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModelTrait.scala index 1425fe19b..23342a60e 100644 --- a/obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModelTrait.scala +++ b/obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModelTrait.scala @@ -469,6 +469,16 @@ trait CustomerMessage { def transport : Option[String] = None //TODO, introduced from V400, may set mandatory later, need to check V140. } +trait AtmAttributeTrait { + def bankId: BankId + def atmId: AtmId + def atmAttributeId: String + def attributeType: AtmAttributeType.Value + def name: String + def value: String + def isActive: Option[Boolean] +} + trait BankAttributeTrait { def bankId: BankId def bankAttributeId: String diff --git a/obp-commons/src/main/scala/com/openbankproject/commons/model/enums/Enumerations.scala b/obp-commons/src/main/scala/com/openbankproject/commons/model/enums/Enumerations.scala index d242d4870..28ff90cc5 100644 --- a/obp-commons/src/main/scala/com/openbankproject/commons/model/enums/Enumerations.scala +++ b/obp-commons/src/main/scala/com/openbankproject/commons/model/enums/Enumerations.scala @@ -15,7 +15,13 @@ object UserAttributeType extends OBPEnumeration[UserAttributeType]{ object DOUBLE extends Value object DATE_WITH_DAY extends Value } - +sealed trait AtmAttributeType extends EnumValue +object AtmAttributeType extends OBPEnumeration[AtmAttributeType]{ + object STRING extends Value + object INTEGER extends Value + object DOUBLE extends Value + object DATE_WITH_DAY extends Value +} sealed trait BankAttributeType extends EnumValue object BankAttributeType extends OBPEnumeration[BankAttributeType]{ object STRING extends Value From bf02a2c7bd2ff6299109b1a90c9a22b13b577a89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Fri, 31 Mar 2023 09:38:16 +0200 Subject: [PATCH 08/11] test/Added tests regarding Create ATM Attributes Endpoints --- .../code/api/v5_1_0/AtmAttributeTest.scala | 153 ++++++++++++++++++ .../code/api/v5_1_0/V510ServerSetup.scala | 18 ++- 2 files changed, 170 insertions(+), 1 deletion(-) create mode 100644 obp-api/src/test/scala/code/api/v5_1_0/AtmAttributeTest.scala diff --git a/obp-api/src/test/scala/code/api/v5_1_0/AtmAttributeTest.scala b/obp-api/src/test/scala/code/api/v5_1_0/AtmAttributeTest.scala new file mode 100644 index 000000000..8009f16e7 --- /dev/null +++ b/obp-api/src/test/scala/code/api/v5_1_0/AtmAttributeTest.scala @@ -0,0 +1,153 @@ +package code.api.v5_1_0 + +import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON._ +import code.api.util.APIUtil.OAuth._ +import code.api.util.ApiRole._ +import code.api.util.ErrorMessages +import code.api.util.ErrorMessages.UserHasMissingRoles +import code.api.v5_1_0.APIMethods510.Implementations5_1_0 +import code.setup.DefaultUsers +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 AtmAttributeTest extends V510ServerSetup with DefaultUsers { + + override def beforeAll() { + super.beforeAll() + } + + override def afterAll() { + super.afterAll() + } + + /** + * 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.createAtmAttribute)) + object ApiEndpoint2 extends Tag(nameOf(Implementations5_1_0.updateAtmAttribute)) + object ApiEndpoint3 extends Tag(nameOf(Implementations5_1_0.deleteAtmAttribute)) + object ApiEndpoint4 extends Tag(nameOf(Implementations5_1_0.getAtmAttributes)) + object ApiEndpoint5 extends Tag(nameOf(Implementations5_1_0.getAtmAttribute)) + + lazy val bankId = randomBankId + lazy val atmId = createAtmAtBank(bankId).id.getOrElse("") + + feature(s"Assuring that endpoint $ApiEndpoint1 works as expected - $VersionOfApi") { + scenario(s"We try to consume endpoint $ApiEndpoint1 - Anonymous access", ApiEndpoint1, VersionOfApi) { + When("We make the request") + val requestGet = (v5_1_0_Request / "banks" / bankId / "atms" / atmId / "attributes").POST + val responseGet = makePostRequest(requestGet, write(atmAttributeJsonV510)) + Then("We should get a 401") + And("We should get a message: " + ErrorMessages.UserNotLoggedIn) + responseGet.code should equal(401) + responseGet.body.extract[ErrorMessage].message should equal(ErrorMessages.UserNotLoggedIn) + } + scenario(s"We try to consume endpoint $ApiEndpoint1 without proper role - Authorized access", ApiEndpoint1, VersionOfApi) { + When("We make the request") + val requestGet = (v5_1_0_Request / "banks" / bankId / "atms" / atmId / "attributes").POST <@ (user1) + val responseGet = makePostRequest(requestGet, write(atmAttributeJsonV510)) + Then("We should get a 403") + And("We should get a message: " + s"$CanCreateAtmAttribute entitlement required") + responseGet.code should equal(403) + responseGet.body.extract[ErrorMessage].message should startWith(UserHasMissingRoles + CanCreateAtmAttribute) + } + } + + + feature(s"Assuring that endpoint $ApiEndpoint2 works as expected - $VersionOfApi") { + scenario(s"We try to consume endpoint $ApiEndpoint2 - Anonymous access", ApiEndpoint2, VersionOfApi) { + When("We make the request") + val requestGet = (v5_1_0_Request / "banks" / bankId / "atms" / atmId / "attributes" / "DOES_NOT_MATTER").PUT + val responseGet = makePutRequest(requestGet, write(atmAttributeJsonV510)) + Then("We should get a 401") + And("We should get a message: " + ErrorMessages.UserNotLoggedIn) + responseGet.code should equal(401) + responseGet.body.extract[ErrorMessage].message should equal(ErrorMessages.UserNotLoggedIn) + } + scenario(s"We try to consume endpoint $ApiEndpoint2 without proper role - Authorized access", ApiEndpoint2, VersionOfApi) { + When("We make the request") + val requestGet = (v5_1_0_Request / "banks" / bankId / "atms" / atmId / "attributes" / "DOES_NOT_MATTER").PUT <@ (user1) + val responseGet = makePutRequest(requestGet, write(atmAttributeJsonV510)) + Then("We should get a 403") + And("We should get a message: " + s"$CanUpdateAtmAttribute entitlement required") + responseGet.code should equal(403) + responseGet.body.extract[ErrorMessage].message should startWith(UserHasMissingRoles + CanUpdateAtmAttribute) + } + } + + + + feature(s"Assuring that endpoint $ApiEndpoint3 works as expected - $VersionOfApi") { + scenario(s"We try to consume endpoint $ApiEndpoint3 - Anonymous access", ApiEndpoint3, VersionOfApi) { + When("We make the request") + val request = (v5_1_0_Request / "banks" / bankId / "atms" / atmId / "attributes" / "DOES_NOT_MATTER").DELETE + val response = makeDeleteRequest(request) + Then("We should get a 401") + And("We should get a message: " + ErrorMessages.UserNotLoggedIn) + response.code should equal(401) + response.body.extract[ErrorMessage].message should equal(ErrorMessages.UserNotLoggedIn) + } + scenario(s"We try to consume endpoint $ApiEndpoint3 without proper role - Authorized access", ApiEndpoint3, VersionOfApi) { + When("We make the request") + val request = (v5_1_0_Request / "banks" / bankId / "atms" / atmId / "attributes" / "DOES_NOT_MATTER").DELETE <@ (user1) + val response = makeDeleteRequest(request) + Then("We should get a 403") + And("We should get a message: " + s"$CanDeleteAtmAttribute entitlement required") + response.code should equal(403) + response.body.extract[ErrorMessage].message should startWith(UserHasMissingRoles + CanDeleteAtmAttribute) + } + } + + + feature(s"Assuring that endpoint $ApiEndpoint4 works as expected - $VersionOfApi") { + scenario(s"We try to consume endpoint $ApiEndpoint4 - Anonymous access", ApiEndpoint4, VersionOfApi) { + When("We make the request") + val request = (v5_1_0_Request / "banks" / bankId / "atms" / atmId / "attributes").GET + val response = makeGetRequest(request) + Then("We should get a 401") + And("We should get a message: " + ErrorMessages.UserNotLoggedIn) + response.code should equal(401) + response.body.extract[ErrorMessage].message should equal(ErrorMessages.UserNotLoggedIn) + } + scenario(s"We try to consume endpoint $ApiEndpoint4 without proper role - Authorized access", ApiEndpoint4, VersionOfApi) { + When("We make the request") + val request = (v5_1_0_Request / "banks" / bankId / "atms" / atmId / "attributes").GET <@ (user1) + val response = makeGetRequest(request) + Then("We should get a 403") + And("We should get a message: " + s"$CanGetAtmAttribute entitlement required") + response.code should equal(403) + response.body.extract[ErrorMessage].message should startWith(UserHasMissingRoles + CanGetAtmAttribute) + } + } + + feature(s"Assuring that endpoint $ApiEndpoint5 works as expected - $VersionOfApi") { + scenario(s"We try to consume endpoint $ApiEndpoint4 - Anonymous access", ApiEndpoint5, VersionOfApi) { + When("We make the request") + val request = (v5_1_0_Request / "banks" / bankId / "atms" / atmId / "attributes" / "DOES_NOT_MATTER").GET + val response = makeGetRequest(request) + Then("We should get a 401") + And("We should get a message: " + ErrorMessages.UserNotLoggedIn) + response.code should equal(401) + response.body.extract[ErrorMessage].message should equal(ErrorMessages.UserNotLoggedIn) + } + scenario(s"We try to consume endpoint $ApiEndpoint5 without proper role - Authorized access", ApiEndpoint5, VersionOfApi) { + When("We make the request") + val request = (v5_1_0_Request / "banks" / bankId / "atms" / atmId / "attributes" / "DOES_NOT_MATTER").GET <@ (user1) + val response = makeGetRequest(request) + Then("We should get a 403") + And("We should get a message: " + s"$CanGetAtmAttribute entitlement required") + response.code should equal(403) + response.body.extract[ErrorMessage].message should startWith(UserHasMissingRoles + CanGetAtmAttribute) + } + } + + + } \ No newline at end of file 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 bf9edddfc..2df9daee6 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 @@ -1,11 +1,15 @@ package code.api.v5_1_0 +import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON import code.api.util.APIUtil.OAuth.{Consumer, Token, _} +import code.api.util.ApiRole import code.api.v2_0_0.BasicAccountsJSON -import code.api.v4_0_0.BanksJson400 +import code.api.v4_0_0.{AtmJsonV400, BanksJson400} +import code.entitlement.Entitlement import code.setup.{APIResponse, DefaultUsers, ServerSetupWithTestData} import com.openbankproject.commons.util.ApiShortVersions import dispatch.Req +import net.liftweb.json.Serialization.write import scala.util.Random.nextInt @@ -27,6 +31,18 @@ trait V510ServerSetup extends ServerSetupWithTestData with DefaultUsers { val bank = banksJson.banks(randomPosition) bank.id } + + def createAtmAtBank(bankId: String): AtmJsonV400 = { + val postAtmJson = SwaggerDefinitionsJSON.atmJsonV400.copy(bank_id = bankId) + val entitlement = Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanCreateAtmAtAnyBank.toString) + val requestCreateAtm = (v4_0_0_Request / "banks" / bankId / "atms").POST <@ (user1) + val responseCreateAtm = makePostRequest(requestCreateAtm, write(postAtmJson)) + val responseBodyCreateAtm = responseCreateAtm.body.extract[AtmJsonV400] + responseBodyCreateAtm should be (postAtmJson) + responseCreateAtm.code should be (201) + Entitlement.entitlement.vend.deleteEntitlement(entitlement) + responseBodyCreateAtm + } def getPrivateAccounts(bankId : String, consumerAndToken: Option[(Consumer, Token)]) : APIResponse = { val request = v5_1_0_Request / "banks" / bankId / "accounts" / "private" <@(consumerAndToken) //TODO, how can we know which endpoint it called? Although it is V300, but this endpoint called V200-privateAccountsAtOneBank From 4beddf21078a13454b40397f18d1ce2da66e91c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Fri, 31 Mar 2023 09:38:59 +0200 Subject: [PATCH 09/11] test/Added tests regarding Create ATM Attributes Endpoints --- .../code/api/v5_1_0/AtmAttributeTest.scala | 60 ++++++++++++++++++- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/obp-api/src/test/scala/code/api/v5_1_0/AtmAttributeTest.scala b/obp-api/src/test/scala/code/api/v5_1_0/AtmAttributeTest.scala index 8009f16e7..e4b8de72d 100644 --- a/obp-api/src/test/scala/code/api/v5_1_0/AtmAttributeTest.scala +++ b/obp-api/src/test/scala/code/api/v5_1_0/AtmAttributeTest.scala @@ -3,9 +3,10 @@ package code.api.v5_1_0 import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON._ import code.api.util.APIUtil.OAuth._ import code.api.util.ApiRole._ -import code.api.util.ErrorMessages -import code.api.util.ErrorMessages.UserHasMissingRoles +import code.api.util.{ApiRole, ErrorMessages} +import code.api.util.ErrorMessages.{AtmNotFoundByAtmId, UserHasMissingRoles} import code.api.v5_1_0.APIMethods510.Implementations5_1_0 +import code.entitlement.Entitlement import code.setup.DefaultUsers import com.github.dwickern.macros.NameOf.nameOf import com.openbankproject.commons.model.ErrorMessage @@ -59,6 +60,17 @@ class AtmAttributeTest extends V510ServerSetup with DefaultUsers { responseGet.code should equal(403) responseGet.body.extract[ErrorMessage].message should startWith(UserHasMissingRoles + CanCreateAtmAttribute) } + scenario(s"We try to consume endpoint $ApiEndpoint1 with proper role but invalid ATM - Authorized access", ApiEndpoint1, VersionOfApi) { + When("We make the request") + val entitlement = Entitlement.entitlement.vend.addEntitlement(bankId, resourceUser1.userId, ApiRole.CanCreateAtmAttribute.toString) + val requestGet = (v5_1_0_Request / "banks" / bankId / "atms" / "atmId-invalid" / "attributes").POST <@ (user1) + val responseGet = makePostRequest(requestGet, write(atmAttributeJsonV510)) + Then("We should get a 404") + And("We should get a message: " + s"$AtmNotFoundByAtmId") + responseGet.code should equal(404) + responseGet.body.extract[ErrorMessage].message should startWith(AtmNotFoundByAtmId) + Entitlement.entitlement.vend.deleteEntitlement(entitlement) + } } @@ -81,6 +93,17 @@ class AtmAttributeTest extends V510ServerSetup with DefaultUsers { responseGet.code should equal(403) responseGet.body.extract[ErrorMessage].message should startWith(UserHasMissingRoles + CanUpdateAtmAttribute) } + scenario(s"We try to consume endpoint $ApiEndpoint2 with proper role but invalid ATM - Authorized access", ApiEndpoint2, VersionOfApi) { + When("We make the request") + val entitlement = Entitlement.entitlement.vend.addEntitlement(bankId, resourceUser1.userId, ApiRole.CanUpdateAtmAttribute.toString) + val requestGet = (v5_1_0_Request / "banks" / bankId / "atms" / "atmId-invalid" / "attributes" / "DOES_NOT_MATTER").PUT <@ (user1) + val responseGet = makePutRequest(requestGet, write(atmAttributeJsonV510)) + Then("We should get a 404") + And("We should get a message: " + s"$AtmNotFoundByAtmId") + responseGet.code should equal(404) + responseGet.body.extract[ErrorMessage].message should startWith(AtmNotFoundByAtmId) + Entitlement.entitlement.vend.deleteEntitlement(entitlement) + } } @@ -104,6 +127,17 @@ class AtmAttributeTest extends V510ServerSetup with DefaultUsers { response.code should equal(403) response.body.extract[ErrorMessage].message should startWith(UserHasMissingRoles + CanDeleteAtmAttribute) } + scenario(s"We try to consume endpoint $ApiEndpoint3 with proper role but invalid ATM - Authorized access", ApiEndpoint3, VersionOfApi) { + When("We make the request") + val entitlement = Entitlement.entitlement.vend.addEntitlement(bankId, resourceUser1.userId, ApiRole.CanDeleteAtmAttribute.toString) + val request = (v5_1_0_Request / "banks" / bankId / "atms" / "atmId-invalid" / "attributes" / "DOES_NOT_MATTER").DELETE <@ (user1) + val response = makeDeleteRequest(request) + Then("We should get a 404") + And("We should get a message: " + s"$AtmNotFoundByAtmId") + response.code should equal(404) + response.body.extract[ErrorMessage].message should startWith(AtmNotFoundByAtmId) + Entitlement.entitlement.vend.deleteEntitlement(entitlement) + } } @@ -126,6 +160,17 @@ class AtmAttributeTest extends V510ServerSetup with DefaultUsers { response.code should equal(403) response.body.extract[ErrorMessage].message should startWith(UserHasMissingRoles + CanGetAtmAttribute) } + scenario(s"We try to consume endpoint $ApiEndpoint4 with proper role but invalid ATM - Authorized access", ApiEndpoint4, VersionOfApi) { + When("We make the request") + val entitlement = Entitlement.entitlement.vend.addEntitlement(bankId, resourceUser1.userId, ApiRole.CanGetAtmAttribute.toString) + val request = (v5_1_0_Request / "banks" / bankId / "atms" / "atmId-invalid" / "attributes").GET <@ (user1) + val response = makeGetRequest(request) + Then("We should get a 404") + And("We should get a message: " + s"$AtmNotFoundByAtmId") + response.code should equal(404) + response.body.extract[ErrorMessage].message should startWith(AtmNotFoundByAtmId) + Entitlement.entitlement.vend.deleteEntitlement(entitlement) + } } feature(s"Assuring that endpoint $ApiEndpoint5 works as expected - $VersionOfApi") { @@ -147,6 +192,17 @@ class AtmAttributeTest extends V510ServerSetup with DefaultUsers { response.code should equal(403) response.body.extract[ErrorMessage].message should startWith(UserHasMissingRoles + CanGetAtmAttribute) } + scenario(s"We try to consume endpoint $ApiEndpoint5 with proper role but invalid ATM - Authorized access", ApiEndpoint5, VersionOfApi) { + When("We make the request") + val entitlement = Entitlement.entitlement.vend.addEntitlement(bankId, resourceUser1.userId, ApiRole.CanGetAtmAttribute.toString) + val request = (v5_1_0_Request / "banks" / bankId / "atms" / "atmId-invalid" / "attributes" / "DOES_NOT_MATTER").GET <@ (user1) + val response = makeGetRequest(request) + Then("We should get a 404") + And("We should get a message: " + s"$AtmNotFoundByAtmId") + response.code should equal(404) + response.body.extract[ErrorMessage].message should startWith(AtmNotFoundByAtmId) + Entitlement.entitlement.vend.deleteEntitlement(entitlement) + } } From 293ce957c4486677eedf4831c9604d187d04ed9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Fri, 31 Mar 2023 12:50:35 +0200 Subject: [PATCH 10/11] docfix/Tweak Bank Attributes Endpoints --- .../code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala | 2 ++ obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala | 4 ++-- 2 files changed, 4 insertions(+), 2 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 663e262a2..1e7a319b4 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 @@ -4013,6 +4013,8 @@ object SwaggerDefinitionsJSON { value = "2012-04-23", is_active = Some(true) ) + val bankAttributesResponseJsonV400 = BankAttributesResponseJsonV400(List(bankAttributeResponseJsonV400)) + val atmAttributeResponseJsonV510 = AtmAttributeResponseJsonV510( bank_id = bankIdExample.value, atm_id = atmIdExample.value, 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 54d1e0ff5..f9962d81b 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 @@ -6624,7 +6624,7 @@ trait APIMethods400 { | |""", EmptyBody, - transactionAttributesResponseJson, + bankAttributesResponseJsonV400, List( $UserNotLoggedIn, $BankNotFound, @@ -6659,7 +6659,7 @@ trait APIMethods400 { | |""", EmptyBody, - transactionAttributesResponseJson, + bankAttributeResponseJsonV400, List( $UserNotLoggedIn, $BankNotFound, From f21d86a9906f52a417321f681b3e0e5a8c5e313d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Fri, 31 Mar 2023 21:02:17 +0200 Subject: [PATCH 11/11] docfix/Tweak Bank Attributes Endpoints 2 --- obp-api/src/main/scala/code/api/v5_1_0/JSONFactory5.1.0.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 c111eceb8..ee9919a1a 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 @@ -108,7 +108,7 @@ case class AtmAttributeResponseJsonV510( value: String, is_active: Option[Boolean] ) -case class AtmAttributesResponseJsonV510(bank_attributes: List[AtmAttributeResponseJsonV510]) +case class AtmAttributesResponseJsonV510(atm_attributes: List[AtmAttributeResponseJsonV510]) case class AtmAttributeBankResponseJsonV510(name: String, value: String) case class AtmAttributesResponseJson(list: List[AtmAttributeBankResponseJsonV510])