From 80f6b4a7c1fa445315d77458a62d9ee496968deb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Thu, 29 May 2025 14:20:55 +0200 Subject: [PATCH 1/6] feature/Tweak BG error messages if consent is not found --- .../berlin/group/v1_3/AccountInformationServiceAISApi.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 11b2ff93ae..9390b1dc67 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 @@ -266,7 +266,7 @@ recurringIndicator: } consumerIdFromConsent = consent.mConsumerId.get consumerIdFromCurrentCall = callContext.map(_.consumer.map(_.consumerId.get).getOrElse("None")).getOrElse("None") - _ <- Helper.booleanToFuture(failMsg = s"$ConsentNotFound $consumerIdFromConsent != $consumerIdFromCurrentCall", failCode = 403, cc = cc.callContext) { + _ <- Helper.booleanToFuture(failMsg = ConsentNotFound, failCode = 403, cc = cc.callContext) { consumerIdFromConsent == consumerIdFromCurrentCall } _ <- Future(Consents.consentProvider.vend.revokeBerlinGroupConsent(consentId)) map { @@ -742,7 +742,7 @@ where the consent was directly managed between ASPSP and PSU e.g. in a re-direct } consumerIdFromConsent = consent.mConsumerId.get consumerIdFromCurrentCall = callContext.map(_.consumer.map(_.consumerId.get).getOrElse("None")).getOrElse("None") - _ <- Helper.booleanToFuture(failMsg = s"$ConsentNotFound $consumerIdFromConsent != $consumerIdFromCurrentCall", failCode = 403, cc = cc.callContext) { + _ <- Helper.booleanToFuture(failMsg = ConsentNotFound, failCode = 403, cc = cc.callContext) { consumerIdFromConsent == consumerIdFromCurrentCall } } yield { From ef3673af39ddb443fc5c8a34130989b9954727d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Thu, 29 May 2025 14:26:06 +0200 Subject: [PATCH 2/6] bugfix/Wrong consent status after revoke consent --- obp-api/src/main/scala/code/api/util/ConsentUtil.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 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 2b7b9eb5f1..675cb018bf 100644 --- a/obp-api/src/main/scala/code/api/util/ConsentUtil.scala +++ b/obp-api/src/main/scala/code/api/util/ConsentUtil.scala @@ -1125,9 +1125,9 @@ object Consent extends MdcLoggable { By(MappedConsent.mUserId, consent.userId), // for the same PSU By(MappedConsent.mConsumerId, consent.consumerId), // from the same TPP ).filterNot(_.consentId == consent.consentId) // Exclude current consent - .map{ c => // Set to expired - val changedStatus = c.mStatus(ConsentStatus.expired.toString).mLastActionDate(new Date()).save - if(changedStatus) logger.warn(s"|---> Changed status to ${ConsentStatus.expired.toString} for consent ID: ${c.id}") + .map{ c => // Set to terminatedByTpp + val changedStatus = c.mStatus(ConsentStatus.terminatedByTpp.toString).mLastActionDate(new Date()).save + if(changedStatus) logger.warn(s"|---> Changed status to ${ConsentStatus.terminatedByTpp.toString} for consent ID: ${c.id}") changedStatus }.forall(_ == true) } else { From 13b6ff7a896474b0cda827de31924aff1444031f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Fri, 30 May 2025 10:04:06 +0200 Subject: [PATCH 3/6] feature/Bump scala compiler from 2.12.12 to 2.12.20 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b1ab5ea61f..4d96472c8b 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ 2011 2.12 - 2.12.12 + 2.12.20 2.5.32 1.8.2 3.5.0 From 2719d0e4692d06dadf74d0e349902ba21f4dfebb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Mon, 2 Jun 2025 13:31:59 +0200 Subject: [PATCH 4/6] feature/Add consumer secret to json response of createConsumer v5.1.0 This reverts commit 4ed3ba76f9e318854c44ce12ac18ef013979268f. --- .../code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala | 1 + obp-api/src/main/scala/code/api/v5_1_0/JSONFactory5.1.0.scala | 2 ++ 2 files changed, 3 insertions(+) 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 49a9728c2f..166b4c5038 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 @@ -2714,6 +2714,7 @@ object SwaggerDefinitionsJSON { lazy val consumerJsonV510: ConsumerJsonV510 = ConsumerJsonV510( consumer_id = consumerIdExample.value, consumer_key = consumerKeyExample.value, + consumer_secret = consumerSecretExample.value, app_name = appNameExample.value, app_type = appTypeExample.value, description = descriptionExample.value, 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 8d4980bb2a..0eb3441aba 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 @@ -445,6 +445,7 @@ case class ConsumerPostJsonV510(app_name: Option[String], ) case class ConsumerJsonV510(consumer_id: String, consumer_key: String, + consumer_secret: String, app_name: String, app_type: String, description: String, @@ -1080,6 +1081,7 @@ object JSONFactory510 extends CustomJsonFormats { ConsumerJsonV510( consumer_id = c.consumerId.get, consumer_key = c.key.get, + consumer_secret = c.secret.get, app_name = c.name.get, app_type = c.appType.toString(), description = c.description.get, From f3133ebb4adadb31d9e31c31bc2d50ec76aa0863 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Tue, 3 Jun 2025 16:14:36 +0200 Subject: [PATCH 5/6] feature/96 - TPP requests without PSU involvement --- .../scala/code/api/util/ConsentUtil.scala | 43 +++++++++++-------- 1 file changed, 24 insertions(+), 19 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 675cb018bf..4d40f75bc2 100644 --- a/obp-api/src/main/scala/code/api/util/ConsentUtil.scala +++ b/obp-api/src/main/scala/code/api/util/ConsentUtil.scala @@ -532,25 +532,30 @@ object Consent extends MdcLoggable { } def checkFrequencyPerDay(storedConsent: consent.ConsentTrait) = { - def isSameDay(date1: Date, date2: Date): Boolean = { - val fmt = new SimpleDateFormat("yyyyMMdd") - fmt.format(date1).equals(fmt.format(date2)) - } - var usesSoFarTodayCounter = storedConsent.usesSoFarTodayCounter - storedConsent.recurringIndicator match { - case false => // The consent is for one access to the account data - if(usesSoFarTodayCounter == 0) // Maximum value is "1". - (true, 0) // All good - else - (false, 1) // Exceeded rate limit - case true => // The consent is for recurring access to the account data - if(!isSameDay(storedConsent.usesSoFarTodayCounterUpdatedAt, new Date())) { - usesSoFarTodayCounter = 0 // Reset counter - } - if(usesSoFarTodayCounter < storedConsent.frequencyPerDay) - (true, usesSoFarTodayCounter) // All good - else - (false, storedConsent.frequencyPerDay) // Exceeded rate limit + if(BerlinGroupCheck.isTppRequestsWithoutPsuInvolvement(callContext.requestHeaders)) { + def isSameDay(date1: Date, date2: Date): Boolean = { + val fmt = new SimpleDateFormat("yyyyMMdd") + fmt.format(date1).equals(fmt.format(date2)) + } + + var usesSoFarTodayCounter = storedConsent.usesSoFarTodayCounter + storedConsent.recurringIndicator match { + case false => // The consent is for one access to the account data + if (usesSoFarTodayCounter == 0) // Maximum value is "1". + (true, 0) // All good + else + (false, 1) // Exceeded rate limit + case true => // The consent is for recurring access to the account data + if (!isSameDay(storedConsent.usesSoFarTodayCounterUpdatedAt, new Date())) { + usesSoFarTodayCounter = 0 // Reset counter + } + if (usesSoFarTodayCounter < storedConsent.frequencyPerDay) + (true, usesSoFarTodayCounter) // All good + else + (false, storedConsent.frequencyPerDay) // Exceeded rate limit + } + } else { + (true, 0) // All good } } From b383e80e2a8867857e3713c3901e43ffc6dbdcc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Wed, 4 Jun 2025 11:55:46 +0200 Subject: [PATCH 6/6] feature/Add ConsumerJsonOnlyForPostResponseV510 --- .../SwaggerDefinitionsJSON.scala | 16 +++++++ .../scala/code/api/v5_1_0/APIMethods510.scala | 4 +- .../code/api/v5_1_0/JSONFactory5.1.0.scala | 47 ++++++++++++++++++- 3 files changed, 64 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 166b4c5038..96a669c463 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 @@ -2712,6 +2712,22 @@ object SwaggerDefinitionsJSON { ) lazy val consumerJsonV510: ConsumerJsonV510 = ConsumerJsonV510( + consumer_id = consumerIdExample.value, + consumer_key = consumerKeyExample.value, + app_name = appNameExample.value, + app_type = appTypeExample.value, + description = descriptionExample.value, + developer_email = emailExample.value, + company = companyExample.value, + redirect_url = redirectUrlExample.value, + certificate_pem = pem, + certificate_info = Some(certificateInfoJsonV510), + created_by_user = resourceUserJSON, + enabled = true, + created = DateWithDayExampleObject, + logo_url = Some(logoURLExample.value) + ) + lazy val consumerJsonOnlyForPostResponseV510: ConsumerJsonOnlyForPostResponseV510 = ConsumerJsonOnlyForPostResponseV510( consumer_id = consumerIdExample.value, consumer_key = consumerKeyExample.value, consumer_secret = consumerSecretExample.value, 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 405b7e0971..5cbe6b86b4 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 @@ -3097,7 +3097,7 @@ trait APIMethods510 { |-----END CERTIFICATE-----""".stripMargin, Some("logoUrl") ), - consumerJsonV510, + consumerJsonOnlyForPostResponseV510, List( UserNotLoggedIn, UserHasMissingRoles, @@ -3134,7 +3134,7 @@ trait APIMethods510 { callContext ) } yield { - (JSONFactory510.createConsumerJSON(consumer, None), HttpCode.`201`(callContext)) + (JSONFactory510.createConsumerJsonOnlyForPostResponseV510(consumer, None), HttpCode.`201`(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 0eb3441aba..89f750e4e8 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 @@ -445,7 +445,6 @@ case class ConsumerPostJsonV510(app_name: Option[String], ) case class ConsumerJsonV510(consumer_id: String, consumer_key: String, - consumer_secret: String, app_name: String, app_type: String, description: String, @@ -459,6 +458,22 @@ case class ConsumerJsonV510(consumer_id: String, created: Date, logo_url: Option[String] ) +case class ConsumerJsonOnlyForPostResponseV510(consumer_id: String, + consumer_key: String, + consumer_secret: String, + app_name: String, + app_type: String, + description: String, + developer_email: String, + company: String, + redirect_url: String, + certificate_pem: String, + certificate_info: Option[CertificateInfoJsonV510], + created_by_user: ResourceUserJSON, + enabled: Boolean, + created: Date, + logo_url: Option[String] + ) case class ConsumersJsonV510( consumers : List[ConsumerJsonV510] @@ -1079,6 +1094,36 @@ object JSONFactory510 extends CustomJsonFormats { } ConsumerJsonV510( + consumer_id = c.consumerId.get, + consumer_key = c.key.get, + app_name = c.name.get, + app_type = c.appType.toString(), + description = c.description.get, + developer_email = c.developerEmail.get, + company = c.company.get, + redirect_url = c.redirectURL.get, + certificate_pem = c.clientCertificate.get, + certificate_info = certificateInfo, + created_by_user = resourceUserJSON, + enabled = c.isActive.get, + created = c.createdAt.get, + logo_url = if (c.logoUrl.get == null || c.logoUrl.get.isEmpty ) null else Some(c.logoUrl.get) + ) + } + def createConsumerJsonOnlyForPostResponseV510(c: Consumer, certificateInfo: Option[CertificateInfoJsonV510] = None): ConsumerJsonOnlyForPostResponseV510 = { + + val resourceUserJSON = Users.users.vend.getUserByUserId(c.createdByUserId.toString()) match { + case Full(resourceUser) => ResourceUserJSON( + user_id = resourceUser.userId, + email = resourceUser.emailAddress, + provider_id = resourceUser.idGivenByProvider, + provider = resourceUser.provider, + username = resourceUser.name + ) + case _ => null + } + + ConsumerJsonOnlyForPostResponseV510( consumer_id = c.consumerId.get, consumer_key = c.key.get, consumer_secret = c.secret.get,