Merge remote-tracking branch 'upstream/develop' into develop

This commit is contained in:
Marko Milić 2025-01-24 16:46:53 +01:00
commit 60a9f76a57
27 changed files with 2414 additions and 938 deletions

View File

@ -1359,5 +1359,13 @@ validate_iban=false
# sample props regulated_entities = [{"certificate_authority_ca_owner_id":"CY_CBC","entity_certificate_public_key":"-----BEGIN CERTIFICATE-----MIICsjCCAZqgAwIBAgIGAYwQ62R0MA0GCSqGSIb3DQEBCwUAMBoxGDAWBgNVBAMMD2FwcC5leGFtcGxlLmNvbTAeFw0yMzExMjcxMzE1MTFaFw0yNTExMjYxMzE1MTFaMBoxGDAWBgNVBAMMD2FwcC5leGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK9WIodZHWzKyCcf9YfWEhPURbfO6zKuMqzHN27GdqHsVVEGxP4F/J4mso+0ENcRr6ur4u81iREaVdCc40rHDHVJNEtniD8Icbz7tcsqAewIVhc/q6WXGqImJpCq7hA0m247dDsaZT0lb/MVBiMoJxDEmAE/GYYnWTEn84R35WhJsMvuQ7QmLvNg6RkChY6POCT/YKe9NKwa1NqI1U+oA5RFzAaFtytvZCE3jtp+aR0brL7qaGfgxm6B7dEpGyhg0NcVCV7xMQNq2JxZTVdAr6lcsRGaAFulakmW3aNnmK+L35Wu8uW+OxNxwUuC6f3b4FVBa276FMuUTRfu7gc+k6kCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAAU5CjEyAoyTn7PgFpQD48ZNPuUsEQ19gzYgJvHMzFIoZ7jKBodjO5mCzWBcR7A4mpeAsdyiNBl2sTiZscSnNqxk61jVzP5Ba1D7XtOjjr7+3iqowrThj6BY40QqhYh/6BSY9fDzVZQiHnvlo6ZUM5kUK6OavZOovKlp5DIl5sGqoP0qAJnpQ4nhB2WVVsKfPlOXc+2KSsbJ23g9l8zaTMr+X0umlvfEKqyEl1Fa2L1dO0y/KFQ+ILmxcZLpRdq1hRAjd0quq9qGC8ucXhRWDgM4hslVpau0da68g0aItWNez3mc5lB82b3dcZpFMzO41bgw7gvw10AvvTfQDqEYIuQ==-----END CERTIFICATE-----","entity_code":"PSD_PICY_CBC!12345","entity_type":"PSD_PI","entity_address":"EXAMPLE COMPANY LTD, 5 SOME STREET","entity_town_city":"SOME CITY","entity_post_code":"1060","entity_country":"CY","entity_web_site":"www.example.com","services":[{"CY":["PS_010","PS_020","PS_03C","PS_04C"]}]}]
regulated_entities = []
#In OBP Create Consent if the app that is creating the consent (grantor_consumer_id) wants to create a consent for the grantee_consumer_id App, then we should skip SCA.
#The use case is API Explorer II giving a consent to Opey . In such a case API Explorer II and Opey are effectively the same App as far as the user is concerned.
#skip_consent_sca_for_consumer_id_pairs=[{ \
# "grantor_consumer_id": "ef0a8fa4-3814-4a21-8ca9-8c553a43aa631", \
# "grantee_consumer_id": "fb327484-94d7-44d2-83e5-8d27301e8279" \
#}]
# Note: For secure and http only settings for cookies see resources/web.xml which is mentioned in the README.md

View File

@ -593,7 +593,9 @@ class Boot extends MdcLoggable {
OAuthWorkedThanks.menu, //OAuth thanks page that will do the redirect
Menu.i("Introduction") / "introduction",
Menu.i("add-user-auth-context-update-request") / "add-user-auth-context-update-request",
Menu.i("confirm-user-auth-context-update-request") / "confirm-user-auth-context-update-request"
Menu.i("confirm-user-auth-context-update-request") / "confirm-user-auth-context-update-request",
Menu.i("confirm-vrp-consent-request") / "confirm-vrp-consent-request" >> AuthUser.loginFirst,//OAuth consent page,
Menu.i("confirm-vrp-consent") / "confirm-vrp-consent" >> AuthUser.loginFirst //OAuth consent page
) ++ accountCreation ++ Admin.menus
// Build SiteMap

View File

@ -5240,6 +5240,14 @@ object SwaggerDefinitionsJSON {
consent_request_id = Some(consentRequestIdExample.value),
account_access= Some(consentAccountAccessJson)
)
val consentJsonV510 = ConsentJsonV510(
consent_id = "9d429899-24f5-42c8-8565-943ffa6a7945",
jwt = "eyJhbGciOiJIUzI1NiJ9.eyJlbnRpdGxlbWVudHMiOltdLCJjcmVhdGVkQnlVc2VySWQiOiJhYjY1MzlhOS1iMTA1LTQ0ODktYTg4My0wYWQ4ZDZjNjE2NTciLCJzdWIiOiIyMWUxYzhjYy1mOTE4LTRlYWMtYjhlMy01ZTVlZWM2YjNiNGIiLCJhdWQiOiJlanpuazUwNWQxMzJyeW9tbmhieDFxbXRvaHVyYnNiYjBraWphanNrIiwibmJmIjoxNTUzNTU0ODk5LCJpc3MiOiJodHRwczpcL1wvd3d3Lm9wZW5iYW5rcHJvamVjdC5jb20iLCJleHAiOjE1NTM1NTg0OTksImlhdCI6MTU1MzU1NDg5OSwianRpIjoiMDlmODhkNWYtZWNlNi00Mzk4LThlOTktNjYxMWZhMWNkYmQ1Iiwidmlld3MiOlt7ImFjY291bnRfaWQiOiJtYXJrb19wcml2aXRlXzAxIiwiYmFua19pZCI6ImdoLjI5LnVrLngiLCJ2aWV3X2lkIjoib3duZXIifSx7ImFjY291bnRfaWQiOiJtYXJrb19wcml2aXRlXzAyIiwiYmFua19pZCI6ImdoLjI5LnVrLngiLCJ2aWV3X2lkIjoib3duZXIifV19.8cc7cBEf2NyQvJoukBCmDLT7LXYcuzTcSYLqSpbxLp4",
status = ConsentStatus.INITIATED.toString,
consent_request_id = Some(consentRequestIdExample.value),
scopes = None,
consumer_id= consumerIdExample.value
)
val postConsentRequestJsonV500 = PostConsentRequestJsonV500(
everything = false,

View File

@ -504,7 +504,7 @@ There are the following request types on this access path:
})
}
SigningBasketX.signingBasketProvider.vend.saveSigningBasketStatus(basketId, ConstantsBG.SigningBasketsStatus.ACTC.toString)
unboxFullOrFail(boxedChallenge, callContext, s"$InvalidConnectorResponse ")
unboxFullOrFail(boxedChallenge, callContext, s"$InvalidConnectorResponse validateChallengeAnswerC5")
} else { // Fail due to unexisting payment
val paymentIds = basket.flatMap(_.payments).getOrElse(Nil).mkString(",")
unboxFullOrFail(Empty, callContext, s"$InvalidConnectorResponse Some of paymentIds [${paymentIds}] are invalid")
@ -520,10 +520,10 @@ There are the following request types on this access path:
})
}
// Fail in case of an error message
unboxFullOrFail(boxedChallenge, callContext, s"$InvalidConnectorResponse ")
unboxFullOrFail(boxedChallenge, callContext, s"$InvalidConnectorResponse validateChallengeAnswerC5")
}
case _ => // Fail in case of an error message
Future(unboxFullOrFail(Empty, callContext, s"$InvalidConnectorResponse "))
Future(unboxFullOrFail(Empty, callContext, s"$InvalidConnectorResponse getChallenge"))
}
} yield {
(JSONFactory_BERLIN_GROUP_1_3.createStartPaymentAuthorisationJson(challenge), callContext)

View File

@ -601,6 +601,11 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
domain: String,
bank_ids: List[String]
)
//This is used for get the value from props `skip_consent_sca_for_consumer_id_pairs`
case class ConsumerIdPair(
grantor_consumer_id: String,
grantee_consumer_id: String
)
case class EmailDomainToEntitlementMapping(
domain: String,
@ -3167,7 +3172,7 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
}
def connectorEmptyResponse[T](box: Box[T], cc: Option[CallContext])(implicit m: Manifest[T]): T = {
unboxFullOrFail(box, cc, InvalidConnectorResponse, 400)
unboxFullOrFail(box, cc, s"$InvalidConnectorResponse ${nameOf(connectorEmptyResponse _)}" , 400)
}
def unboxFuture[T](box: Box[Future[T]]): Future[Box[T]] = box match {
@ -4729,6 +4734,23 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
APIUtil.getPropsValue("email_domain_to_space_mappings").map(extractor).getOrElse(Nil)
}
val skipConsentScaForConsumerIdPairs: List[ConsumerIdPair] = {
def extractor(str: String) = try {
val consumerIdPair = json.parse(str).extract[List[ConsumerIdPair]]
//The props value can be parsed to JNothing.
if(str.nonEmpty && consumerIdPair == Nil)
throw new RuntimeException("props [skip_consent_sca_for_consumer_id_pairs] parse -> extract to Nil!")
else
consumerIdPair
} catch {
case e: Throwable => // error handling, found wrong props value as early as possible.
this.logger.error(s"props [skip_consent_sca_for_consumer_id_pairs] value is invalid, it should be the class($ConsumerIdPair) json format, current value is $str ." );
throw e;
}
APIUtil.getPropsValue("skip_consent_sca_for_consumer_id_pairs").map(extractor).getOrElse(Nil)
}
val emailDomainToEntitlementMappings: List[EmailDomainToEntitlementMapping] = {
def extractor(str: String) = try {
val emailDomainToEntitlementMappings = json.parse(str).extract[List[EmailDomainToEntitlementMapping]]

View File

@ -824,7 +824,7 @@ object ExampleValue {
lazy val line1Example = ConnectorField(NoExampleProvided,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("line1", line1Example)
lazy val fromDateExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
lazy val fromDateExample = ConnectorField(DateWithMsExampleString,s"The TimeStamp in the format: $DateWithMs")
glossaryItems += makeGlossaryItem("from_date", fromDateExample)
lazy val creditLimitExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
@ -908,7 +908,7 @@ object ExampleValue {
lazy val cashWithdrawalNationalFeeExample = ConnectorField(NoExampleProvided, NoDescriptionProvided)
glossaryItems += makeGlossaryItem("ATM.cash_withdrawal_national_fee", cashWithdrawalNationalFeeExample)
lazy val cashWithdrawalInternationalFeeExample = ConnectorField(NoExampleProvided, NoDescriptionProvided)
lazy val cashWithdrawalInternationalFeeExample: ConnectorField = ConnectorField(NoExampleProvided, NoDescriptionProvided)
glossaryItems += makeGlossaryItem("ATM.cash_withdrawal_international_fee", cashWithdrawalInternationalFeeExample)
lazy val balanceInquiryFeeExample = ConnectorField(NoExampleProvided, NoDescriptionProvided)
@ -1052,7 +1052,7 @@ object ExampleValue {
lazy val itemsExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("items", itemsExample)
lazy val toDateExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
lazy val toDateExample = ConnectorField(DateWithMsExampleString,s"The TimeStamp in the format: $DateWithMs")
glossaryItems += makeGlossaryItem("to_date", toDateExample)
lazy val bankRoutingsExample = ConnectorField("bank routing in form of (scheme, address)",NoDescriptionProvided)
@ -1622,7 +1622,7 @@ object ExampleValue {
lazy val endDateExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("end_date", endDateExample)
lazy val canAddTransactionRequestToOwnAccountExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
lazy val canAddTransactionRequestToOwnAccountExample = ConnectorField(booleanFalse,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("can_add_transaction_request_to_own_account", canAddTransactionRequestToOwnAccountExample)
lazy val otherAccountRoutingAddressExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
@ -2340,7 +2340,7 @@ object ExampleValue {
lazy val inboundAdapterInfoInternalErrorCodeExample = ConnectorField("error code", "fix me")
lazy val inboundAdapterInfoInternalNameExample = ConnectorField("NAME", "fix me")
lazy val inboundAdapterInfoInternalGit_commitExample = ConnectorField("git_commit", "fix me")
lazy val inboundAdapterInfoInternalDateExample = ConnectorField("date String", "fix me")
lazy val inboundAdapterInfoInternalDateExample = ConnectorField(DateWithMsExampleString, "")
lazy val inboundAdapterInfoInternalVersionExample = ConnectorField("version string", "fix me")
lazy val inboundStatusMessageStatusExample = ConnectorField("Status string", "fix me")

View File

@ -1,12 +1,12 @@
package code.api.util.newstyle
import code.api.util.APIUtil.{OBPReturnType, unboxFullOrFail}
import code.api.util.ErrorMessages.InvalidConnectorResponseForGetBankAccounts
import code.api.util.ErrorMessages.{InvalidConnectorResponse}
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 com.github.dwickern.macros.NameOf.nameOf
import scala.concurrent.Future
object BalanceNewStyle {
@ -46,13 +46,13 @@ object BalanceNewStyle {
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)
(unboxFullOrFail(i._1, callContext,s"$InvalidConnectorResponse ${nameOf(getBankAccountBalances _)} ", 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)
(unboxFullOrFail(i._1, callContext,s"$InvalidConnectorResponse ${nameOf(getBankAccountsBalances _)}", 400 ), i._2)
}
}

View File

@ -28,7 +28,7 @@ import code.api.v3_0_0.JSONFactory300.createAdapterInfoJson
import code.api.v3_1_0.JSONFactory310._
import code.bankconnectors.rest.RestConnector_vMar2019
import code.bankconnectors.{Connector, LocalMappedConnector}
import code.consent.{ConsentRequests, ConsentStatus, Consents}
import code.consent.{ConsentRequests, ConsentStatus, Consents, MappedConsent}
import code.consumer.Consumers
import code.context.UserAuthContextUpdateProvider
import code.entitlement.Entitlement
@ -57,6 +57,7 @@ import net.liftweb.http.provider.HTTPParam
import net.liftweb.http.rest.RestHelper
import net.liftweb.json._
import net.liftweb.util.Helpers.tryo
import net.liftweb.mapper.By
import net.liftweb.util.Mailer.{From, PlainMailBodyType, Subject, To}
import net.liftweb.util.{Helpers, Mailer, Props, StringHelpers}
import org.apache.commons.lang3.{StringUtils, Validate}
@ -3586,66 +3587,82 @@ trait APIMethods310 {
_ <- Future(Consents.consentProvider.vend.setJsonWebToken(createdConsent.consentId, consentJWT)) map {
i => connectorEmptyResponse(i, callContext)
}
challengeText = s"Your consent challenge : ${challengeAnswer}, Application: $applicationText"
_ <- scaMethod match {
case v if v == StrongCustomerAuthentication.EMAIL.toString => // Send the email
for{
failMsg <- Future {s"$InvalidJsonFormat The Json body should be the $PostConsentEmailJsonV310"}
postConsentEmailJson <- NewStyle.function.tryons(failMsg, 400, callContext) {
json.extract[PostConsentEmailJsonV310]
}
(status, callContext) <- NewStyle.function.sendCustomerNotification(
StrongCustomerAuthentication.EMAIL,
postConsentEmailJson.email,
Some("OBP Consent Challenge"),
challengeText,
callContext
)
} yield Future{status}
case v if v == StrongCustomerAuthentication.SMS.toString =>
for {
failMsg <- Future {
s"$InvalidJsonFormat The Json body should be the $PostConsentPhoneJsonV310"
}
postConsentPhoneJson <- NewStyle.function.tryons(failMsg, 400, callContext) {
json.extract[PostConsentPhoneJsonV310]
}
phoneNumber = postConsentPhoneJson.phone_number
(status, callContext) <- NewStyle.function.sendCustomerNotification(
StrongCustomerAuthentication.SMS,
phoneNumber,
None,
challengeText,
callContext
)
} yield Future{status}
case v if v == StrongCustomerAuthentication.IMPLICIT.toString =>
for {
(consentImplicitSCA, callContext) <- NewStyle.function.getConsentImplicitSCA(user, callContext)
status <- consentImplicitSCA.scaMethod match {
case v if v == StrongCustomerAuthentication.EMAIL => // Send the email
NewStyle.function.sendCustomerNotification (
StrongCustomerAuthentication.EMAIL,
consentImplicitSCA.recipient,
Some ("OBP Consent Challenge"),
challengeText,
callContext
)
case v if v == StrongCustomerAuthentication.SMS =>
NewStyle.function.sendCustomerNotification(
StrongCustomerAuthentication.SMS,
consentImplicitSCA.recipient,
None,
challengeText,
callContext
)
case _ => Future {
"Success"
}
}} yield {
status
//we need to check `skip_consent_sca_for_consumer_id_pairs` props, to see if we really need the SCA flow.
//this is from callContext
grantorConsumerId = callContext.map(_.consumer.toOption.map(_.consumerId.get)).flatten.getOrElse("Unknown")
//this is from json body
granteeConsumerId = consentJson.consumer_id.getOrElse("Unknown")
shouldSkipConsentScaForConsumerIdPair = APIUtil.skipConsentScaForConsumerIdPairs.contains(
APIUtil.ConsumerIdPair(
grantorConsumerId,
granteeConsumerId
))
mappedConsent <- if (shouldSkipConsentScaForConsumerIdPair) {
Future{
MappedConsent.find(By(MappedConsent.mConsentId, createdConsent.consentId)).map(_.mStatus(ConsentStatus.ACCEPTED.toString).saveMe()).head
}
case _ =>Future{"Success"}
} else {
val challengeText = s"Your consent challenge : ${challengeAnswer}, Application: $applicationText"
scaMethod match {
case v if v == StrongCustomerAuthentication.EMAIL.toString => // Send the email
for{
failMsg <- Future {s"$InvalidJsonFormat The Json body should be the $PostConsentEmailJsonV310"}
postConsentEmailJson <- NewStyle.function.tryons(failMsg, 400, callContext) {
json.extract[PostConsentEmailJsonV310]
}
(status, callContext) <- NewStyle.function.sendCustomerNotification(
StrongCustomerAuthentication.EMAIL,
postConsentEmailJson.email,
Some("OBP Consent Challenge"),
challengeText,
callContext
)
} yield createdConsent
case v if v == StrongCustomerAuthentication.SMS.toString =>
for {
failMsg <- Future {
s"$InvalidJsonFormat The Json body should be the $PostConsentPhoneJsonV310"
}
postConsentPhoneJson <- NewStyle.function.tryons(failMsg, 400, callContext) {
json.extract[PostConsentPhoneJsonV310]
}
phoneNumber = postConsentPhoneJson.phone_number
(status, callContext) <- NewStyle.function.sendCustomerNotification(
StrongCustomerAuthentication.SMS,
phoneNumber,
None,
challengeText,
callContext
)
} yield createdConsent
case v if v == StrongCustomerAuthentication.IMPLICIT.toString =>
for {
(consentImplicitSCA, callContext) <- NewStyle.function.getConsentImplicitSCA(user, callContext)
status <- consentImplicitSCA.scaMethod match {
case v if v == StrongCustomerAuthentication.EMAIL => // Send the email
NewStyle.function.sendCustomerNotification (
StrongCustomerAuthentication.EMAIL,
consentImplicitSCA.recipient,
Some ("OBP Consent Challenge"),
challengeText,
callContext
)
case v if v == StrongCustomerAuthentication.SMS =>
NewStyle.function.sendCustomerNotification(
StrongCustomerAuthentication.SMS,
consentImplicitSCA.recipient,
None,
challengeText,
callContext
)
case _ => Future {
"Success"
}
}} yield {
createdConsent
}
case _ =>Future{createdConsent}}
}
} yield {
(ConsentJsonV310(createdConsent.consentId, consentJWT, createdConsent.status), HttpCode.`201`(callContext))

View File

@ -18,7 +18,7 @@ import code.api.v4_0_0.{JSONFactory400, PostCounterpartyJson400}
import code.api.v5_0_0.JSONFactory500.{createPhysicalCardJson, createViewJsonV500, createViewsIdsJsonV500, createViewsJsonV500}
import code.api.v5_1_0.{CreateCustomViewJson, PostCounterpartyLimitV510, PostVRPConsentRequestJsonV510}
import code.bankconnectors.Connector
import code.consent.{ConsentRequests, Consents}
import code.consent.{ConsentRequests, ConsentStatus, Consents, MappedConsent}
import code.consumer.Consumers
import code.entitlement.Entitlement
import code.metadata.counterparties.MappedCounterparty
@ -40,6 +40,7 @@ import net.liftweb.json
import net.liftweb.json.{Extraction, compactRender, prettyRender}
import net.liftweb.util.Helpers.tryo
import net.liftweb.util.{Helpers, Props, StringHelpers}
import net.liftweb.mapper.By
import java.util.UUID
import java.util.concurrent.ThreadLocalRandom
@ -929,7 +930,7 @@ trait APIMethods500 {
)) map {
i => unboxFullOrFail(i,callContext, ConsentRequestNotFound)
}
_ <- Helper.booleanToFuture(ConsentRequestIsInvalid, cc=callContext){
_ <- Helper.booleanToFuture(s"$ConsentRequestIsInvalid, the current CONSENT_REQUEST_ID($consentRequestId) is already used to create a consent, please provide another one!", cc=callContext){
Consents.consentProvider.vend.getConsentByConsentRequestId(consentRequestId).isEmpty
}
_ <- Helper.booleanToFuture(ConsentAllowedScaMethods, cc=callContext){
@ -1202,34 +1203,54 @@ trait APIMethods500 {
_ <- Future(Consents.consentProvider.vend.setJsonWebToken(createdConsent.consentId, consentJWT)) map {
i => connectorEmptyResponse(i, callContext)
}
challengeText = s"Your consent challenge : ${challengeAnswer}, Application: $applicationText"
_ <- scaMethod match {
case v if v == StrongCustomerAuthentication.EMAIL.toString => // Send the email
sendEmailNotification(callContext, consentRequestJson, challengeText)
case v if v == StrongCustomerAuthentication.SMS.toString =>
sendSmsNotification(callContext, consentRequestJson, challengeText)
case v if v == StrongCustomerAuthentication.IMPLICIT.toString =>
for {
(consentImplicitSCA, callContext) <- NewStyle.function.getConsentImplicitSCA(user, callContext)
status <- consentImplicitSCA.scaMethod match {
case v if v == StrongCustomerAuthentication.EMAIL => // Send the email
sendEmailNotification(callContext, consentRequestJson.copy(email=Some(consentImplicitSCA.recipient)), challengeText)
case v if v == StrongCustomerAuthentication.SMS =>
sendSmsNotification(callContext, consentRequestJson.copy(phone_number=Some(consentImplicitSCA.recipient)), challengeText)
case _ => Future {
"Success"
}
}} yield {
status
//we need to check `skip_consent_sca_for_consumer_id_pairs` props, to see if we really need the SCA flow.
//this is from callContext
grantorConsumerId = callContext.map(_.consumer.toOption.map(_.consumerId.get)).flatten.getOrElse("Unknown")
//this is from json body
granteeConsumerId = postConsentBodyCommonJson.consumer_id.getOrElse("Unknown")
shouldSkipConsentScaForConsumerIdPair = APIUtil.skipConsentScaForConsumerIdPairs.contains(
APIUtil.ConsumerIdPair(
grantorConsumerId,
granteeConsumerId
))
mappedConsent <- if (shouldSkipConsentScaForConsumerIdPair) {
Future{
MappedConsent.find(By(MappedConsent.mConsentId, createdConsent.consentId)).map(_.mStatus(ConsentStatus.ACCEPTED.toString).saveMe()).head
}
} else {
val challengeText = s"Your consent challenge : ${challengeAnswer}, Application: $applicationText"
scaMethod match {
case v if v == StrongCustomerAuthentication.EMAIL.toString => // Send the email
sendEmailNotification(callContext, consentRequestJson, challengeText)
case v if v == StrongCustomerAuthentication.SMS.toString =>
sendSmsNotification(callContext, consentRequestJson, challengeText)
case v if v == StrongCustomerAuthentication.IMPLICIT.toString =>
for {
(consentImplicitSCA, callContext) <- NewStyle.function.getConsentImplicitSCA(user, callContext)
status <- consentImplicitSCA.scaMethod match {
case v if v == StrongCustomerAuthentication.EMAIL => // Send the email
sendEmailNotification(callContext, consentRequestJson.copy(email = Some(consentImplicitSCA.recipient)), challengeText)
case v if v == StrongCustomerAuthentication.SMS =>
sendSmsNotification(callContext, consentRequestJson.copy(phone_number = Some(consentImplicitSCA.recipient)), challengeText)
case _ => Future {
"Success"
}
}} yield {
status
}
case _ => Future {
"Success"
}
case _ =>Future{"Success"}
}
Future{createdConsent}
}
} yield {
(ConsentJsonV500(
createdConsent.consentId,
mappedConsent.consentId,
consentJWT,
createdConsent.status,
Some(createdConsent.consentRequestId),
mappedConsent.status,
Some(mappedConsent.consentRequestId),
if (isVRPConsentRequest) Some(ConsentAccountAccessJson(bankId.value, accountId.value, viewId.value, HelperInfoJson(List(counterpartyId.value)))) else None
), HttpCode.`201`(callContext))
}

View File

@ -1684,7 +1684,7 @@ trait APIMethods510 {
|
""".stripMargin,
EmptyBody,
consentJsonV500,
consentJsonV510,
List(
$UserNotLoggedIn,
UnknownError
@ -2050,77 +2050,95 @@ trait APIMethods510 {
_ <- Future(Consents.consentProvider.vend.setJsonWebToken(createdConsent.consentId, consentJWT)) map {
i => connectorEmptyResponse(i, callContext)
}
challengeText = s"Your consent challenge : ${challengeAnswer}, Application: $applicationText"
_ <- scaMethod match {
case v if v == StrongCustomerAuthentication.EMAIL.toString => // Send the email
for {
failMsg <- Future {
s"$InvalidJsonFormat The Json body should be the $PostConsentEmailJsonV310"
}
postConsentEmailJson <- NewStyle.function.tryons(failMsg, 400, callContext) {
json.extract[PostConsentEmailJsonV310]
}
(status, callContext) <- NewStyle.function.sendCustomerNotification(
StrongCustomerAuthentication.EMAIL,
postConsentEmailJson.email,
Some("OBP Consent Challenge"),
challengeText,
callContext
)
} yield Future {
status
}
case v if v == StrongCustomerAuthentication.SMS.toString =>
for {
failMsg <- Future {
s"$InvalidJsonFormat The Json body should be the $PostConsentPhoneJsonV310"
}
postConsentPhoneJson <- NewStyle.function.tryons(failMsg, 400, callContext) {
json.extract[PostConsentPhoneJsonV310]
}
phoneNumber = postConsentPhoneJson.phone_number
(status, callContext) <- NewStyle.function.sendCustomerNotification(
StrongCustomerAuthentication.SMS,
phoneNumber,
None,
challengeText,
callContext
)
} yield Future {
status
}
case v if v == StrongCustomerAuthentication.IMPLICIT.toString =>
for {
(consentImplicitSCA, callContext) <- NewStyle.function.getConsentImplicitSCA(user, callContext)
status <- consentImplicitSCA.scaMethod match {
case v if v == StrongCustomerAuthentication.EMAIL => // Send the email
NewStyle.function.sendCustomerNotification(
StrongCustomerAuthentication.EMAIL,
consentImplicitSCA.recipient,
Some("OBP Consent Challenge"),
challengeText,
callContext
)
case v if v == StrongCustomerAuthentication.SMS =>
NewStyle.function.sendCustomerNotification(
StrongCustomerAuthentication.SMS,
consentImplicitSCA.recipient,
None,
challengeText,
callContext
)
case _ => Future {
"Success"
//we need to check `skip_consent_sca_for_consumer_id_pairs` props, to see if we really need the SCA flow.
//this is from callContext
grantorConsumerId = callContext.map(_.consumer.toOption.map(_.consumerId.get)).flatten.getOrElse("Unknown")
//this is from json body
granteeConsumerId = consentJson.consumer_id.getOrElse("Unknown")
shouldSkipConsentScaForConsumerIdPair = APIUtil.skipConsentScaForConsumerIdPairs.contains(
APIUtil.ConsumerIdPair(
grantorConsumerId,
granteeConsumerId
))
mappedConsent <- if (shouldSkipConsentScaForConsumerIdPair) {
Future{
MappedConsent.find(By(MappedConsent.mConsentId, createdConsent.consentId)).map(_.mStatus(ConsentStatus.ACCEPTED.toString).saveMe()).head
}
} else {
val challengeText = s"Your consent challenge : ${challengeAnswer}, Application: $applicationText"
scaMethod match {
case v if v == StrongCustomerAuthentication.EMAIL.toString => // Send the email
for {
failMsg <- Future {
s"$InvalidJsonFormat The Json body should be the $PostConsentEmailJsonV310"
}
}} yield {
status
postConsentEmailJson <- NewStyle.function.tryons(failMsg, 400, callContext) {
json.extract[PostConsentEmailJsonV310]
}
(status, callContext) <- NewStyle.function.sendCustomerNotification(
StrongCustomerAuthentication.EMAIL,
postConsentEmailJson.email,
Some("OBP Consent Challenge"),
challengeText,
callContext
)
} yield {
createdConsent
}
case v if v == StrongCustomerAuthentication.SMS.toString =>
for {
failMsg <- Future {
s"$InvalidJsonFormat The Json body should be the $PostConsentPhoneJsonV310"
}
postConsentPhoneJson <- NewStyle.function.tryons(failMsg, 400, callContext) {
json.extract[PostConsentPhoneJsonV310]
}
phoneNumber = postConsentPhoneJson.phone_number
(status, callContext) <- NewStyle.function.sendCustomerNotification(
StrongCustomerAuthentication.SMS,
phoneNumber,
None,
challengeText,
callContext
)
} yield {
createdConsent
}
case v if v == StrongCustomerAuthentication.IMPLICIT.toString =>
for {
(consentImplicitSCA, callContext) <- NewStyle.function.getConsentImplicitSCA(user, callContext)
status <- consentImplicitSCA.scaMethod match {
case v if v == StrongCustomerAuthentication.EMAIL => // Send the email
NewStyle.function.sendCustomerNotification(
StrongCustomerAuthentication.EMAIL,
consentImplicitSCA.recipient,
Some("OBP Consent Challenge"),
challengeText,
callContext
)
case v if v == StrongCustomerAuthentication.SMS =>
NewStyle.function.sendCustomerNotification(
StrongCustomerAuthentication.SMS,
consentImplicitSCA.recipient,
None,
challengeText,
callContext
)
case _ => Future {
"Success"
}
}} yield {
createdConsent
}
case _ => Future {
createdConsent
}
case _ => Future {
"Success"
}
}
} yield {
(ConsentJsonV310(createdConsent.consentId, consentJWT, createdConsent.status), HttpCode.`201`(callContext))
(ConsentJsonV310(mappedConsent.consentId, consentJWT, mappedConsent.status), HttpCode.`201`(callContext))
}
}
}

View File

@ -31,8 +31,7 @@ import code.api.berlin.group.v1_3.JSONFactory_BERLIN_GROUP_1_3.ConsentAccessJson
import code.api.util.APIUtil.{DateWithDay, DateWithSeconds, gitCommit, stringOrNull}
import code.api.util._
import code.api.v1_2_1.BankRoutingJsonV121
import code.api.v1_4_0.JSONFactory1_4_0.{LocationJsonV140, MetaJsonV140, transformToLocationFromV140, transformToMetaFromV140,
TransactionRequestAccountJsonV140,ChallengeJsonV140}
import code.api.v1_4_0.JSONFactory1_4_0.{ChallengeJsonV140, LocationJsonV140, MetaJsonV140, TransactionRequestAccountJsonV140, transformToLocationFromV140, transformToMetaFromV140}
import code.api.v2_0_0.TransactionRequestChargeJsonV200
import code.api.v2_1_0.ResourceUserJSON
import code.api.v3_0_0.JSONFactory300.{createLocationJson, createMetaJson, transformToAddressFromV300}
@ -121,7 +120,8 @@ case class ConsentJsonV510(consent_id: String,
jwt: String,
status: String,
consent_request_id: Option[String],
scopes: Option[List[Role]])
scopes: Option[List[Role]],
consumer_id:String)
case class ConsentInfoJsonV510(consent_id: String,
@ -859,7 +859,8 @@ object JSONFactory510 extends CustomJsonFormats {
consent.jsonWebToken,
consent.status,
Some(consent.consentRequestId),
jsonWebTokenAsJValue.map(_.entitlements).toOption
jsonWebTokenAsJValue.map(_.entitlements).toOption,
consent.consumerId
)
}

View File

@ -303,6 +303,19 @@ trait Connector extends MdcLoggable {
callContext: Option[CallContext]
): OBPReturnType[Box[AmountOfMoney]] =Future{(Failure(setUnimplementedError(nameOf(getChallengeThreshold _))), callContext)}
//TODO. WIP
def shouldRaiseConsentChallenge(
bankId: String,
accountId: String,
viewId: String,
consentRequestType: String,
userId: String,
username: String,
callContext: Option[CallContext]
): OBPReturnType[Box[(Boolean, String)]] = Future {
(Failure(setUnimplementedError(nameOf(shouldRaiseConsentChallenge _))), callContext)
}
def getPaymentLimit(
bankId: String,
accountId: String,

View File

@ -9,7 +9,6 @@ object CommonsCaseClassGenerator extends App {
//We need to check if the classCommons is existing or not.
val allExistingClasses = getClassesFromPackage("com.openbankproject.commons.model")
.filter(it =>it.getName.endsWith("Commons"))
.toList
val missingReturnModels: Set[ru.Type] = connectorDeclsMethodsReturnOBPRequiredType
@ -17,7 +16,8 @@ object CommonsCaseClassGenerator extends App {
.filter(it => {
val symbol = it.typeSymbol
val isAbstract = symbol.isAbstract
isAbstract //We only need the commons classes for abstract class, eg: ProductAttributeCommons instead of ProductAttribute
isAbstract && //We only need the commons classes for abstract class, eg: ProductAttributeCommons instead of ProductAttribute
!it.toString.equals("Boolean") //Boolean is also abstract class, so we need to remove it.
})
.filterNot(it =>
allExistingClasses.find(thisClass=> thisClass.toString.contains(s"${it.typeSymbol.name}Commons")).isDefined
@ -37,9 +37,13 @@ object CommonsCaseClassGenerator extends App {
}
// private val str: String = ru.typeOf[Bank].decls.map(it => s"${it.name} :${it.typeSignature.typeSymbol.name}").mkString(", \n")
private val caseClassStrings: Set[String] = missingReturnModels.map(mkClass)
println("#################################Started########################################################################")
caseClassStrings.foreach {
println
}
println()
println("#################################Finished########################################################################")
println("Please copy and compair the result to obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModel.scala")
System.exit(0)
}

View File

@ -2,7 +2,7 @@ package code.bankconnectors.generator
import code.api.util.CodeGenerateUtils.createDocExample
import code.api.util.{APIUtil, CallContext}
import code.bankconnectors.Connector
import code.bankconnectors.{Connector, LocalMappedConnector}
import code.bankconnectors.vSept2018.KafkaMappedConnector_vSept2018
import com.openbankproject.commons.util.ReflectUtils
import org.apache.commons.io.FileUtils
@ -21,7 +21,7 @@ import scala.reflect.runtime.{universe => ru}
*/
object ConnectorBuilderUtil {
def getClassesFromPackage(packageName: String): Seq[Class[_]] = {
def getClassesFromPackage(packageName: String): List[Class[_]] = {
val classLoader = Thread.currentThread().getContextClassLoader
val path = packageName.replace('.', '/')
val resources: Seq[URL] = classLoader.getResources(path).asScala.toSeq
@ -36,7 +36,7 @@ object ConnectorBuilderUtil {
} else {
Seq.empty
}
}
}.toList
}
@ -273,165 +273,170 @@ object ConnectorBuilderUtil {
}
}
val commonMethodNames = List(
"getAdapterInfo",
"getChallengeThreshold",
"getChargeLevel",
"getChargeLevelC2",
"createChallenge",
"getBank",
"getBanks",
"getBankAccountsForUser",
"getBankAccountsBalances",
"getBankAccountBalances",
"getCoreBankAccounts",
"getBankAccountsHeld",
"getCounterpartyTrait",
"getCounterpartyByCounterpartyId",
"getCounterpartyByIban",
"getCounterparties",
"getTransactions",
"getTransactionsCore",
"getTransaction",
"getPhysicalCardForBank",
"deletePhysicalCardForBank",
"getPhysicalCardsForBank",
"createPhysicalCard",
"updatePhysicalCard",
"makePaymentv210",
"makePaymentV400",
"cancelPaymentV400",
"createTransactionRequestv210",
"getTransactionRequests210",
"getTransactionRequestImpl",
"createTransactionAfterChallengeV210",
"updateBankAccount",
"createBankAccount",
"getBranch",
"getBranches",
"getAtm",
"getAtms",
"createTransactionAfterChallengev300",
"makePaymentv300",
"createTransactionRequestv300",
"createCounterparty",
"checkCustomerNumberAvailable",
"createCustomer",
"updateCustomerScaData",
"updateCustomerCreditData",
"updateCustomerGeneralData",
"getCustomersByUserId",
"getCustomerByCustomerId",
"getCustomerByCustomerNumber",
"getCustomerAddress",
"createCustomerAddress",
"updateCustomerAddress",
"deleteCustomerAddress",
"createTaxResidence",
"getTaxResidence",
"deleteTaxResidence",
"getCustomers",
"getCheckbookOrders",
"getStatusOfCreditCardOrder",
"createUserAuthContext",
"createUserAuthContextUpdate",
"deleteUserAuthContexts",
"deleteUserAuthContextById",
"getUserAuthContexts",
"createOrUpdateProductAttribute",
"getProduct",
"getProducts",
"getProductAttributeById",
"getProductAttributesByBankAndCode",
"deleteProductAttribute",
"getAccountAttributeById",
"createOrUpdateAccountAttribute",
"createAccountAttributes",
"getAccountAttributesByAccount",
"createOrUpdateCardAttribute",
"getCardAttributeById",
"getCardAttributesFromProvider",
"createAccountApplication",
"getAllAccountApplication",
"getAccountApplicationById",
"updateAccountApplicationStatus",
"getOrCreateProductCollection",
"getProductCollection",
"getOrCreateProductCollectionItem",
"getProductCollectionItem",
"getProductCollectionItemsTree",
"createMeeting",
"getMeetings",
"getMeeting",
"createOrUpdateKycCheck",
"createOrUpdateKycDocument",
"createOrUpdateKycMedia",
"createOrUpdateKycStatus",
"getKycChecks",
"getKycDocuments",
"getKycMedias",
"getKycStatuses",
"createMessage",
"makeHistoricalPayment",
"validateChallengeAnswer",
//"getBankLegacy", // should not generate for Legacy methods
//"getBanksLegacy", // should not generate for Legacy methods
//"getBankAccountsForUserLegacy", // should not generate for Legacy methods
//"getBankAccountLegacy", // should not generate for Legacy methods
"getBankAccountByIban",
"getBankAccountByRouting",
"getBankAccounts",
"checkBankAccountExists",
//"getCoreBankAccountsLegacy", // should not generate for Legacy methods
//"getBankAccountsHeldLegacy", // should not generate for Legacy methods
//"checkBankAccountExistsLegacy", // should not generate for Legacy methods
//"getCounterpartyByCounterpartyIdLegacy", // should not generate for Legacy methods
//"getCounterpartiesLegacy", // should not generate for Legacy methods
//"getTransactionsLegacy", // should not generate for Legacy methods
//"getTransactionLegacy", // should not generate for Legacy methods
//"createPhysicalCardLegacy", // should not generate for Legacy methods
//"getCustomerByCustomerIdLegacy", // should not generate for Legacy methods
//TODO WIP, need to fix the code to support the following methods
// val commonMethodNames = LocalMappedConnector.callableMethods.keySet.toList
"createChallenges",
"createTransactionRequestv400",
"createTransactionRequestSepaCreditTransfersBGV1",
"createTransactionRequestPeriodicSepaCreditTransfersBGV1",
"getCustomersByCustomerPhoneNumber",
"getTransactionAttributeById",
"createOrUpdateCustomerAttribute",
"createOrUpdateTransactionAttribute",
"getCustomerAttributes",
"getCustomerIdsByAttributeNameValues",
"getCustomerAttributesForCustomers",
"getTransactionIdsByAttributeNameValues",
"getTransactionAttributes",
"getBankAttributesByBank",
"getCustomerAttributeById",
"createDirectDebit",
"deleteCustomerAttribute",
"getPhysicalCardsForUser",
"getChallengesByBasketId",
"createChallengesC2",
"createChallengesC3",
"getChallenge",
"getChallengesByTransactionRequestId",
"getChallengesByConsentId",
"validateAndCheckIbanNumber",
"validateChallengeAnswerC2",
"validateChallengeAnswerC3",
"validateChallengeAnswerC4",
"validateChallengeAnswerC5",
"validateChallengeAnswerV2",
"getCounterpartyByIbanAndBankAccountId",
"getChargeValue",
"saveTransactionRequestTransaction",
"saveTransactionRequestChallenge",
"getTransactionRequestTypes",
"updateAccountLabel",
"getProduct",
"saveTransactionRequestStatusImpl",
"getTransactionRequestTypeCharges"
).distinct
val commonMethodNames = List(
"getAdapterInfo",
"getChallengeThreshold",
"getChargeLevel",
"getChargeLevelC2",
"createChallenge",
"getBank",
"getBanks",
"getBankAccountsForUser",
"getBankAccountsBalances",
"getBankAccountBalances",
"getCoreBankAccounts",
"getBankAccountsHeld",
"getCounterpartyTrait",
"getCounterpartyByCounterpartyId",
"getCounterpartyByIban",
"getCounterparties",
"getTransactions",
"getTransactionsCore",
"getTransaction",
"getPhysicalCardForBank",
"deletePhysicalCardForBank",
"getPhysicalCardsForBank",
"createPhysicalCard",
"updatePhysicalCard",
"makePaymentv210",
"makePaymentV400",
"cancelPaymentV400",
"createTransactionRequestv210",
"getTransactionRequests210",
"getTransactionRequestImpl",
"createTransactionAfterChallengeV210",
"updateBankAccount",
"createBankAccount",
"getBranch",
"getBranches",
"getAtm",
"getAtms",
"createTransactionAfterChallengev300",
"makePaymentv300",
"createTransactionRequestv300",
"createCounterparty",
"checkCustomerNumberAvailable",
"createCustomer",
"updateCustomerScaData",
"updateCustomerCreditData",
"updateCustomerGeneralData",
"getCustomersByUserId",
"getCustomerByCustomerId",
"getCustomerByCustomerNumber",
"getCustomerAddress",
"createCustomerAddress",
"updateCustomerAddress",
"deleteCustomerAddress",
"createTaxResidence",
"getTaxResidence",
"deleteTaxResidence",
"getCustomers",
"getCheckbookOrders",
"getStatusOfCreditCardOrder",
"createUserAuthContext",
"createUserAuthContextUpdate",
"deleteUserAuthContexts",
"deleteUserAuthContextById",
"getUserAuthContexts",
"createOrUpdateProductAttribute",
"getProduct",
"getProducts",
"getProductAttributeById",
"getProductAttributesByBankAndCode",
"deleteProductAttribute",
"getAccountAttributeById",
"createOrUpdateAccountAttribute",
"createAccountAttributes",
"getAccountAttributesByAccount",
"createOrUpdateCardAttribute",
"getCardAttributeById",
"getCardAttributesFromProvider",
"createAccountApplication",
"getAllAccountApplication",
"getAccountApplicationById",
"updateAccountApplicationStatus",
"getOrCreateProductCollection",
"getProductCollection",
"getOrCreateProductCollectionItem",
"getProductCollectionItem",
"getProductCollectionItemsTree",
"createMeeting",
"getMeetings",
"getMeeting",
"createOrUpdateKycCheck",
"createOrUpdateKycDocument",
"createOrUpdateKycMedia",
"createOrUpdateKycStatus",
"getKycChecks",
"getKycDocuments",
"getKycMedias",
"getKycStatuses",
"createMessage",
"makeHistoricalPayment",
"validateChallengeAnswer",
//"getBankLegacy", // should not generate for Legacy methods
//"getBanksLegacy", // should not generate for Legacy methods
//"getBankAccountsForUserLegacy", // should not generate for Legacy methods
//"getBankAccountLegacy", // should not generate for Legacy methods
"getBankAccountByIban",
"getBankAccountByRouting",
"getBankAccounts",
"checkBankAccountExists",
//"getCoreBankAccountsLegacy", // should not generate for Legacy methods
//"getBankAccountsHeldLegacy", // should not generate for Legacy methods
//"checkBankAccountExistsLegacy", // should not generate for Legacy methods
//"getCounterpartyByCounterpartyIdLegacy", // should not generate for Legacy methods
//"getCounterpartiesLegacy", // should not generate for Legacy methods
//"getTransactionsLegacy", // should not generate for Legacy methods
//"getTransactionLegacy", // should not generate for Legacy methods
//"createPhysicalCardLegacy", // should not generate for Legacy methods
//"getCustomerByCustomerIdLegacy", // should not generate for Legacy methods
"createChallenges",
"createTransactionRequestv400",
"createTransactionRequestSepaCreditTransfersBGV1",
"createTransactionRequestPeriodicSepaCreditTransfersBGV1",
"getCustomersByCustomerPhoneNumber",
"getTransactionAttributeById",
"createOrUpdateCustomerAttribute",
"createOrUpdateTransactionAttribute",
"getCustomerAttributes",
"getCustomerIdsByAttributeNameValues",
"getCustomerAttributesForCustomers",
"getTransactionIdsByAttributeNameValues",
"getTransactionAttributes",
"getBankAttributesByBank",
"getCustomerAttributeById",
"createDirectDebit",
"deleteCustomerAttribute",
"getPhysicalCardsForUser",
"getChallengesByBasketId",
"createChallengesC2",
"createChallengesC3",
"getChallenge",
"getChallengesByTransactionRequestId",
"getChallengesByConsentId",
"validateAndCheckIbanNumber",
"validateChallengeAnswerC2",
"validateChallengeAnswerC3",
"validateChallengeAnswerC4",
"validateChallengeAnswerC5",
"validateChallengeAnswerV2",
"getCounterpartyByIbanAndBankAccountId",
"getChargeValue",
"saveTransactionRequestTransaction",
"saveTransactionRequestChallenge",
"getTransactionRequestTypes",
"updateAccountLabel",
"getProduct",
"saveTransactionRequestStatusImpl",
"getTransactionRequestTypeCharges",
"getAccountsHeld",
"getAccountsHeldByUser",
).distinct
/**
* these connector methods have special parameter or return type
@ -449,20 +454,51 @@ object ConnectorBuilderUtil {
).distinct
val omitMethods = List(
// "createOrUpdateAttributeDefinition", // should not be auto generated
// "deleteAttributeDefinition", // should not be auto generated
// "getAttributeDefinition", // should not be auto generated
// "createStandingOrder", // should not be auto generated
"createOrUpdateAttributeDefinition", // should not be auto generated
"deleteAttributeDefinition", // should not be auto generated
"getAttributeDefinition", // should not be auto generated
"createStandingOrder", // should not be auto generated
//** the follow 5 methods should not be generated, should create manually
// "dynamicEntityProcess",
// "dynamicEndpointProcess",
// "createDynamicEndpoint",
// "getDynamicEndpoint",
// "getDynamicEndpoints",
// "checkExternalUserCredentials",// this is not a standard connector method.
// "checkExternalUserExists", // this is not a standard connector method.
"dynamicEntityProcess",
"dynamicEndpointProcess",
"createDynamicEndpoint",
"getDynamicEndpoint",
"getDynamicEndpoints",
"checkExternalUserCredentials",// this is not a standard connector method.
"checkExternalUserExists", // this is not a standard connector method.
"getBankAccountByRoutingLegacy",
"getAccountRoutingsByScheme",
"getAccountRouting",
"getBankAccountsWithAttributes",
"getBankSettlementAccounts",
"getCountOfTransactionsFromAccountToCounterparty",
"getStatus",
"createOrUpdateBank",
"createOrUpdateProduct",
"getAllAtms",
"getCurrentCurrencies",
"getAgents",
"getCustomersAtAllBanks",
"createOrUpdateBankAttribute",
"getBankAttribute",
"createOrUpdateAtmAttribute",
"getAtmAttribute",
"getBankAttributeById",
"getAtmAttributeById",
"getUserAttributes",
"getPersonalUserAttributes",
"getNonPersonalUserAttributes",
"getUserAttributesByUsers",
"createOrUpdateUserAttribute",
"getUserAttribute",
"getUserAttributeById",
"deleteUserAttribute",
"getTransactionRequestIdsByAttributeNameValues",
"sendCustomerNotification",
"equals",
"getAtmAttributesByAtm",
"==",
"!=",
).distinct
}

View File

@ -9,7 +9,7 @@ object InOutCaseClassGenerator extends App {
val allExistingClasses = getClassesFromPackage("com.openbankproject.commons.dto")
val code = connectorDeclsMethodsReturnOBPRequiredType
.filter(it => allExistingClasses.toString.contains(s"OutBound${it.name.toString.capitalize} ")) //filter what we have implemented
.filterNot(it => allExistingClasses.toString.contains(s"${it.name.toString.capitalize}")) //find what we have not implemented
.map(it => {
val returnType = it.returnType
val tp = extractReturnModel(returnType)
@ -34,9 +34,10 @@ object InOutCaseClassGenerator extends App {
|case class InBound${it.name.toString.capitalize} (inboundAdapterCallContext: InboundAdapterCallContext, status: Status, data: $payload) extends InBoundTrait[$payload]
""".stripMargin
})
println("#################################Started########################################################################")
code.foreach(println)
println("#################################Finished########################################################################")
println("Please copy and compair the result to obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModel.scala")
println("Please copy and compair the result to obp-commons/src/main/scala/com/openbankproject/commons/dto/JsonsTransfer.scala")
}

View File

@ -77,7 +77,7 @@ class ServerCallback(val ch: Channel) extends DeliverCallback with MdcLoggable{
))
}
//---------------- dynamic start -------------------please don't modify this line
// ---------- created on 2024-10-28T12:26:57Z
// ---------- created on 2025-01-14T11:53:01Z
} else if (obpMessageId.contains("get_adapter_info")) {
val outBound = json.parse(message).extract[OutBoundGetAdapterInfo]
@ -687,6 +687,48 @@ class ServerCallback(val ch: Channel) extends DeliverCallback with MdcLoggable{
)).recoverWith {
case e: Exception => Future(InBoundGetBankAccountsHeld(
inboundAdapterCallContext = InboundAdapterCallContext(
correlationId = outBound.outboundAdapterCallContext.correlationId
),
status = Status(e.getMessage, Nil),
data = null
))
}
} else if (obpMessageId.contains("get_accounts_held")) {
val outBound = json.parse(message).extract[OutBoundGetAccountsHeld]
val obpMappedResponse = code.bankconnectors.LocalMappedConnector.getAccountsHeld(outBound.bankId,outBound.user,None).map(_._1.head)
obpMappedResponse.map(response => InBoundGetAccountsHeld(
inboundAdapterCallContext = InboundAdapterCallContext(
correlationId = outBound.outboundAdapterCallContext.correlationId
),
status = Status("", Nil),
data = response
)).recoverWith {
case e: Exception => Future(InBoundGetAccountsHeld(
inboundAdapterCallContext = InboundAdapterCallContext(
correlationId = outBound.outboundAdapterCallContext.correlationId
),
status = Status(e.getMessage, Nil),
data = null
))
}
} else if (obpMessageId.contains("get_accounts_held_by_user")) {
val outBound = json.parse(message).extract[OutBoundGetAccountsHeldByUser]
val obpMappedResponse = code.bankconnectors.LocalMappedConnector.getAccountsHeldByUser(outBound.user,None).map(_._1.head)
obpMappedResponse.map(response => InBoundGetAccountsHeldByUser(
inboundAdapterCallContext = InboundAdapterCallContext(
correlationId = outBound.outboundAdapterCallContext.correlationId
),
status = Status("", Nil),
data = response
)).recoverWith {
case e: Exception => Future(InBoundGetAccountsHeldByUser(
inboundAdapterCallContext = InboundAdapterCallContext(
correlationId = outBound.outboundAdapterCallContext.correlationId
),
@ -1233,6 +1275,27 @@ class ServerCallback(val ch: Channel) extends DeliverCallback with MdcLoggable{
)).recoverWith {
case e: Exception => Future(InBoundGetTransactionRequestImpl(
inboundAdapterCallContext = InboundAdapterCallContext(
correlationId = outBound.outboundAdapterCallContext.correlationId
),
status = Status(e.getMessage, Nil),
data = null
))
}
} else if (obpMessageId.contains("get_transaction_request_types")) {
val outBound = json.parse(message).extract[OutBoundGetTransactionRequestTypes]
val obpMappedResponse = Future{code.bankconnectors.LocalMappedConnector.getTransactionRequestTypes(outBound.initiator,outBound.fromAccount,None).map(_._1).head}
obpMappedResponse.map(response => InBoundGetTransactionRequestTypes(
inboundAdapterCallContext = InboundAdapterCallContext(
correlationId = outBound.outboundAdapterCallContext.correlationId
),
status = Status("", Nil),
data = response
)).recoverWith {
case e: Exception => Future(InBoundGetTransactionRequestTypes(
inboundAdapterCallContext = InboundAdapterCallContext(
correlationId = outBound.outboundAdapterCallContext.correlationId
),
@ -1450,7 +1513,7 @@ class ServerCallback(val ch: Channel) extends DeliverCallback with MdcLoggable{
data = null
))
}
} else if (obpMessageId.contains("create_transaction_after_challengev300")) {
} else if (obpMessageId.contains("create_transaction_after_challengev300")) {
val outBound = json.parse(message).extract[OutBoundCreateTransactionAfterChallengev300]
val obpMappedResponse = code.bankconnectors.LocalMappedConnector.createTransactionAfterChallengev300(outBound.initiator,outBound.fromAccount,outBound.transReqId,outBound.transactionRequestType,None).map(_._1.head)
@ -2115,6 +2178,27 @@ class ServerCallback(val ch: Channel) extends DeliverCallback with MdcLoggable{
)).recoverWith {
case e: Exception => Future(InBoundCreateOrUpdateProductAttribute(
inboundAdapterCallContext = InboundAdapterCallContext(
correlationId = outBound.outboundAdapterCallContext.correlationId
),
status = Status(e.getMessage, Nil),
data = null
))
}
} else if (obpMessageId.contains("get_bank_attributes_by_bank")) {
val outBound = json.parse(message).extract[OutBoundGetBankAttributesByBank]
val obpMappedResponse = code.bankconnectors.LocalMappedConnector.getBankAttributesByBank(outBound.bankId,None).map(_._1.head)
obpMappedResponse.map(response => InBoundGetBankAttributesByBank(
inboundAdapterCallContext = InboundAdapterCallContext(
correlationId = outBound.outboundAdapterCallContext.correlationId
),
status = Status("", Nil),
data = response
)).recoverWith {
case e: Exception => Future(InBoundGetBankAttributesByBank(
inboundAdapterCallContext = InboundAdapterCallContext(
correlationId = outBound.outboundAdapterCallContext.correlationId
),
@ -3046,8 +3130,8 @@ class ServerCallback(val ch: Channel) extends DeliverCallback with MdcLoggable{
data = null
))
}
// ---------- created on 2024-10-28T12:26:57Z
//---------------- dynamic end ---------------------please don't modify this line
// ---------- created on 2025-01-14T11:53:01Z
//---------------- dynamic end ---------------------please don't modify this line
} else {
Future {
1

View File

@ -7,7 +7,7 @@ import scala.language.postfixOps
object RabbitMQConnectorBuilder extends App {
buildMethods(commonMethodNames,
buildMethods(commonMethodNames.diff(omitMethods),
"src/main/scala/code/bankconnectors/rabbitmq/RabbitMQConnector_vOct2024.scala",
methodName => s"""sendRequest[InBound]("obp_${StringHelpers.snakify(methodName)}", req, callContext)""")
}

View File

@ -70,7 +70,7 @@ trait RabbitMQConnector_vOct2024 extends Connector with MdcLoggable {
val connectorName = "rabbitmq_vOct2024"
//---------------- dynamic start -------------------please don't modify this line
// ---------- created on 2024-12-10T10:42:33Z
// ---------- created on 2025-01-14T19:52:36Z
messageDocs += getAdapterInfoDoc
def getAdapterInfoDoc = MessageDoc(
@ -1154,6 +1154,79 @@ trait RabbitMQConnector_vOct2024 extends Connector with MdcLoggable {
response.map(convertToTuple[List[AccountHeld]](callContext))
}
messageDocs += getAccountsHeldDoc
def getAccountsHeldDoc = MessageDoc(
process = "obp.getAccountsHeld",
messageFormat = messageFormat,
description = "Get Accounts Held",
outboundTopic = None,
inboundTopic = None,
exampleOutboundMessage = (
OutBoundGetAccountsHeld(outboundAdapterCallContext=MessageDocsSwaggerDefinitions.outboundAdapterCallContext,
bankId=BankId(bankIdExample.value),
user= UserCommons(userPrimaryKey=UserPrimaryKey(123),
userId=userIdExample.value,
idGivenByProvider="string",
provider=providerExample.value,
emailAddress=emailAddressExample.value,
name=userNameExample.value,
createdByConsentId=Some("string"),
createdByUserInvitationId=Some("string"),
isDeleted=Some(true),
lastMarketingAgreementSignedDate=Some(toDate(dateExample))))
),
exampleInboundMessage = (
InBoundGetAccountsHeld(inboundAdapterCallContext=MessageDocsSwaggerDefinitions.inboundAdapterCallContext,
status=MessageDocsSwaggerDefinitions.inboundStatus,
data=List( BankIdAccountId(bankId=BankId(bankIdExample.value),
accountId=AccountId(accountIdExample.value))))
),
adapterImplementation = Some(AdapterImplementation("- Core", 1))
)
override def getAccountsHeld(bankId: BankId, user: User, callContext: Option[CallContext]): OBPReturnType[Box[List[BankIdAccountId]]] = {
import com.openbankproject.commons.dto.{InBoundGetAccountsHeld => InBound, OutBoundGetAccountsHeld => OutBound}
val req = OutBound(callContext.map(_.toOutboundAdapterCallContext).orNull, bankId, user)
val response: Future[Box[InBound]] = sendRequest[InBound]("obp_get_accounts_held", req, callContext)
response.map(convertToTuple[List[BankIdAccountId]](callContext))
}
messageDocs += getAccountsHeldByUserDoc
def getAccountsHeldByUserDoc = MessageDoc(
process = "obp.getAccountsHeldByUser",
messageFormat = messageFormat,
description = "Get Accounts Held By User",
outboundTopic = None,
inboundTopic = None,
exampleOutboundMessage = (
OutBoundGetAccountsHeldByUser(outboundAdapterCallContext=MessageDocsSwaggerDefinitions.outboundAdapterCallContext,
user= UserCommons(userPrimaryKey=UserPrimaryKey(123),
userId=userIdExample.value,
idGivenByProvider="string",
provider=providerExample.value,
emailAddress=emailAddressExample.value,
name=userNameExample.value,
createdByConsentId=Some("string"),
createdByUserInvitationId=Some("string"),
isDeleted=Some(true),
lastMarketingAgreementSignedDate=Some(toDate(dateExample))))
),
exampleInboundMessage = (
InBoundGetAccountsHeldByUser(inboundAdapterCallContext=MessageDocsSwaggerDefinitions.inboundAdapterCallContext,
status=MessageDocsSwaggerDefinitions.inboundStatus,
data=List( BankIdAccountId(bankId=BankId(bankIdExample.value),
accountId=AccountId(accountIdExample.value))))
),
adapterImplementation = Some(AdapterImplementation("- Core", 1))
)
override def getAccountsHeldByUser(user: User, callContext: Option[CallContext]): OBPReturnType[Box[List[BankIdAccountId]]] = {
import com.openbankproject.commons.dto.{InBoundGetAccountsHeldByUser => InBound, OutBoundGetAccountsHeldByUser => OutBound}
val req = OutBound(callContext.map(_.toOutboundAdapterCallContext).orNull, user)
val response: Future[Box[InBound]] = sendRequest[InBound]("obp_get_accounts_held_by_user", req, callContext)
response.map(convertToTuple[List[BankIdAccountId]](callContext))
}
messageDocs += checkBankAccountExistsDoc
def checkBankAccountExistsDoc = MessageDoc(
process = "obp.checkBankAccountExists",
@ -6997,8 +7070,8 @@ trait RabbitMQConnector_vOct2024 extends Connector with MdcLoggable {
response.map(convertToTuple[Boolean](callContext))
}
// ---------- created on 2024-12-10T10:42:33Z
//---------------- dynamic end ---------------------please don't modify this line
// ---------- created on 2025-01-14T19:52:36Z
//---------------- dynamic end ---------------------please don't modify this line
private val availableOperation = DynamicEntityOperation.values.map(it => s""""$it"""").mkString("[", ", ", "]")

View File

@ -339,7 +339,7 @@ case class BankAccountExtended(val bankAccount: BankAccount) extends MdcLoggable
if(APIUtil.hasAccountAccess(view, BankIdAccountId(bankId, accountId), user, callContext)) {
for {
(transactions, callContext) <- Connector.connector.vend.getTransactions(bankId, accountId, callContext, queryParams) map {
x => (unboxFullOrFail(x._1, callContext, InvalidConnectorResponse, 400), x._2)
x => (unboxFullOrFail(x._1, callContext, InvalidConnectorResponseForGetTransactions, 400), x._2)
}
} yield {
view.moderateTransactionsWithSameAccount(bank, transactions) match {

View File

@ -0,0 +1,241 @@
/**
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 <http://www.gnu.org/licenses/>.
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.util.APIUtil._
import code.api.util.ErrorMessages.InvalidJsonFormat
import code.api.util.{APIUtil, CustomJsonFormats}
import code.api.v5_1_0.{APIMethods510, ConsentJsonV510}
import code.api.v5_0_0.{APIMethods500, ConsentJsonV500, ConsentRequestResponseJson}
import code.api.v3_1_0.{APIMethods310, ConsentChallengeJsonV310, ConsumerJsonV310}
import code.consent.{ConsentStatus}
import code.util.Helper.{MdcLoggable, ObpS}
import net.liftweb.common.Full
import net.liftweb.http.rest.RestHelper
import net.liftweb.http.{GetRequest, PostRequest, RequestVar, S, SHtml}
import net.liftweb.json
import net.liftweb.json.Formats
import net.liftweb.util.Helpers._
class VrpConsentCreation extends MdcLoggable with RestHelper with APIMethods510 with APIMethods500 with APIMethods310 {
protected implicit override def formats: Formats = CustomJsonFormats.formats
private object otpValue extends RequestVar("123456")
def confirmVrpConsentRequest = {
getConsentRequest match {
case Left(error) => {
S.error(error._1)
"#confirm-vrp-consent-request-form-title *" #> s"Please confirm or deny the following consent request:" &
"#confirm-vrp-consent-request-response-json *" #> s"""""" &
"type=submit" #> ""
}
case Right(response) => {
tryo {json.parse(response).extract[ConsentRequestResponseJson]} match {
case Full(consentRequestResponseJson) =>
"#confirm-vrp-consent-request-form-title *" #> s"Please confirm or deny the following consent request:" &
"#confirm-vrp-consent-request-response-json *" #> s"""${json.prettyRender(json.Extraction.decompose(consentRequestResponseJson.payload))}""" &
"#confirm-vrp-consent-request-confirm-submit-button" #> SHtml.onSubmitUnit(confirmConsentRequestProcess)
case _ =>
"#confirm-vrp-consent-request-form-title *" #> s"Please confirm or deny the following consent request:" &
"#confirm-vrp-consent-request-response-json *" #>
s"""$InvalidJsonFormat The Json body should be the $ConsentRequestResponseJson.
|Please check `Get Consent Request` endpoint separately! """.stripMargin &
"type=submit" #> ""
}
}
}
}
def confirmVrpConsent = {
"#otp-value" #> SHtml.textElem(otpValue) &
"type=submit" #> SHtml.onSubmitUnit(confirmVrpConsentProcess)
}
private def confirmConsentRequestProcess() ={
//1st: we need to call `Create Consent By CONSENT_REQUEST_ID (IMPLICIT)`, this will send OTP to account owner.
callCreateConsentByConsentRequestIdImplicit match {
case Left(error) => {
S.error(error._1)
}
case Right(response) => {
tryo {json.parse(response).extract[ConsentJsonV500]} match {
case Full(consentJsonV500) =>
//2nd: we need to redirect to confirm page to fill the OTP
S.redirectTo(
s"/confirm-vrp-consent?CONSENT_ID=${consentJsonV500.consent_id}"
)
case _ =>
S.error(s"$InvalidJsonFormat The Json body should be the $ConsentJsonV500. " +
s"Please check `Create Consent By CONSENT_REQUEST_ID (IMPLICIT) !")
}
}
}
}
private def callAnswerConsentChallenge: Either[(String, Int), String] = {
val requestParam = List(
ObpS.param("CONSENT_ID")
)
if(requestParam.count(_.isDefined) < requestParam.size) {
return Left(("There are one or many mandatory request parameter not present, please check request parameter: CONSENT_ID", 500))
}
val pathOfEndpoint = List(
"banks",
APIUtil.defaultBankId,//we do not need to get this from URL, it will be easier for the developer.
"consents",
ObpS.param("CONSENT_ID")openOr(""),
"challenge"
)
val requestBody = s"""{"answer":"${otpValue.get}"}"""
val authorisationsResult = callEndpoint(Implementations3_1_0.answerConsentChallenge, pathOfEndpoint, PostRequest, requestBody)
authorisationsResult
}
private def callGetConsentByConsentId(consentId: String): Either[(String, Int), String] = {
val pathOfEndpoint = List(
"user",
"current",
"consents",
consentId,
)
val authorisationsResult = callEndpoint(Implementations5_1_0.getConsentByConsentId, pathOfEndpoint, GetRequest)
authorisationsResult
}
private def callGetConsumer(consumerId: String): Either[(String, Int), String] = {
val pathOfEndpoint = List(
"management",
"consumers",
consumerId,
)
val authorisationsResult = callEndpoint(Implementations5_1_0.getConsumer, pathOfEndpoint, GetRequest)
authorisationsResult
}
private def callCreateConsentByConsentRequestIdImplicit: Either[(String, Int), String] = {
val requestParam = List(
ObpS.param("CONSENT_REQUEST_ID"),
)
if(requestParam.count(_.isDefined) < requestParam.size) {
return Left(("Parameter CONSENT_REQUEST_ID is missing, please set it in the URL", 500))
}
val pathOfEndpoint = List(
"consumer",
"consent-requests",
ObpS.param("CONSENT_REQUEST_ID")openOr(""),
"IMPLICIT",
"consents",
)
val requestBody = s"""{}"""
val authorisationsResult = callEndpoint(Implementations5_0_0.createConsentByConsentRequestIdImplicit, pathOfEndpoint, PostRequest, requestBody)
authorisationsResult
}
private def confirmVrpConsentProcess() ={
//1st: we need to answer challenge and create the consent properly.
callAnswerConsentChallenge match {
case Left(error) => S.error(error._1)
case Right(response) => {
tryo {json.parse(response).extract[ConsentChallengeJsonV310]} match {
case Full(consentChallengeJsonV310) if (consentChallengeJsonV310.status.equals(ConsentStatus.ACCEPTED.toString)) =>
//2nd: we need to call getConsent by consentId --> get the consumerId
callGetConsentByConsentId(consentChallengeJsonV310.consent_id) match {
case Left(error) => S.error(error._1)
case Right(response) => {
tryo {json.parse(response).extract[ConsentJsonV510]} match {
case Full(consentJsonV510) =>
//3rd: get consumer by consumerId
callGetConsumer(consentJsonV510.consumer_id) match {
case Left(error) => S.error(error._1)
case Right(response) => {
tryo {json.parse(response).extract[ConsumerJsonV310]} match {
case Full(consumerJsonV310) =>
//4th: get the redirect url.
val redirectURL = consumerJsonV310.redirect_url.trim
S.redirectTo(s"$redirectURL?CONSENT_REQUEST_ID=${consentJsonV510.consent_request_id.getOrElse("")}")
case _ =>
S.error(s"$InvalidJsonFormat The Json body should be the $ConsumerJsonV310. " +
s"Please check `Get Consumer` !")
}
}
}
case _ =>
S.error(s"$InvalidJsonFormat The Json body should be the $ConsentJsonV510. " +
s"Please check `Get Consent By Consent Id` !")
}
}
}
case Full(consentChallengeJsonV310) =>
S.error(s"Current SCA status is ${consentChallengeJsonV310.status}. Please double check OTP value.")
case _ => S.error(s"$InvalidJsonFormat The Json body should be the $ConsentChallengeJsonV310. " +
s"Please check `Answer Consent Challenge` ! ")
}
}
}
}
private def getConsentRequest: Either[(String, Int), String] = {
val requestParam = List(
ObpS.param("CONSENT_REQUEST_ID"),
)
if(requestParam.count(_.isDefined) < requestParam.size) {
return Left(("Parameter CONSENT_REQUEST_ID is missing, please set it in the URL", 500))
}
val pathOfEndpoint = List(
"consumer",
"consent-requests",
ObpS.param("CONSENT_REQUEST_ID")openOr("")
)
val authorisationsResult = callEndpoint(Implementations5_0_0.getConsentRequest, pathOfEndpoint, GetRequest)
authorisationsResult
}
}

View File

@ -212,18 +212,22 @@ object Helper extends Loggable {
*/
def isValidInternalRedirectUrl(url: String) : Boolean = {
//set the default value is "/" and "/oauth/authorize"
val validUrls = List(
val internalRedirectUrlsWhiteList = List(
"/","/oauth/authorize","/consumer-registration",
"/dummy-user-tokens","/create-sandbox-account",
"/add-user-auth-context-update-request","/otp",
"/terms-and-conditions", "/privacy-policy"
"/terms-and-conditions", "/privacy-policy",
"/confirm-vrp-consent-request",
"/confirm-vrp-consent",
"/consent-screen",
"/consent",
)
//case1: OBP-API login: url = "/"
//case2: API-Explore oauth login: url = "/oauth/authorize?oauth_token=V0JTCDYXWUNTXDZ3VUDNM1HE3Q1PZR2WJ4PURXQA&logUserOut=false"
val extractCleanURL = StringUtils.substringBefore(url, "?")
validUrls.contains(extractCleanURL)
internalRedirectUrlsWhiteList.contains(extractCleanURL)
}
/**

View File

@ -0,0 +1,50 @@
<!--
Open Bank Project - API
Copyright (C) 2011-2025, 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 <http://www.gnu.org/licenses/>.
Email: contact@tesobe.com
TESOBE GmbH
Osloerstrasse 16/17
Berlin 13359, Germany
This product includes software developed at
TESOBE (http://www.tesobe.com/)
by
Hongwei Zhang : Hongwei AT tesobe DOT com
-->
<div data-lift="surround?with=default;at=content">
<div id="confirm-vrp-consent-request-div" data-lift="VrpConsentCreation.confirmVrpConsentRequest">
<form method="post">
<div class="form-group">
<h3 id="confirm-vrp-consent-request-form-title">Please check the VRP Consent Request: </h3>
</div>
<pre id="confirm-vrp-consent-request-response-json">
</pre>
<div class="row">
<input id="confirm-vrp-consent-request-confirm-submit-button" class="btn btn-danger pull-right" type="submit" value="Confirm"
tabindex="0"/>
<a id="confirm-vrp-consent-request-deny-submit-button" class="btn btn-danger pull-right" href="/">Deny</span></a>
</div>
<br>
</form>
</div>
</div>

View File

@ -0,0 +1,49 @@
<!--
Open Bank Project - API
Copyright (C) 2011-2017, 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 <http://www.gnu.org/licenses/>.
Email: contact@tesobe.com
TESOBE GmbH
Osloerstrasse 16/17
Berlin 13359, Germany
This product includes software developed at
TESOBE (http://www.tesobe.com/)
by
Simon Redfern : simon AT tesobe DOT com
Sebastian Henschel : sebastian AT tesobe DOT com
-->
<div data-lift="surround?with=default;at=content">
<div id="confirm-user-auth-context-update-request-div" data-lift="VrpConsentCreation.confirmVrpConsent">
<form class="login" method="post">
<div class="form-group">
<h3>Please enter the One Time Password (OTP) that we just sent to you</h3>
<p>Please check your phone or email for the value to enter.</p>
<input class="form-control" id="otp-value" type="text" value="123" tabindex="0" autofocus
autocomplete="off" aria-label="One Time Password"/>
</div>
<div class="row">
<input id="authorise-submit-button" class="btn btn-danger pull-right" type="submit" value="Submit"
tabindex="0"/>
</div>
<br>
</form>
</div>
</div>

View File

@ -437,4 +437,8 @@ input{
color: #333333;
line-height: 24px;
margin-bottom: 15px;
}
#confirm-vrp-consent-request-deny-submit-button {
background: red;
}

View File

@ -199,7 +199,7 @@ class VRPConsentRequestTest extends V510ServerSetup with PropsReset{
val getConsentByRequestResponse = makeGetRequest(getConsentByRequestIdUrl(consentRequestId))
Then("We should get a 200")
getConsentByRequestResponse.code should equal(200)
val getConsentByRequestResponseJson = getConsentByRequestResponse.body.extract[ConsentJsonV510]
val getConsentByRequestResponseJson = getConsentByRequestResponse.body.extract[ConsentJsonV500]
getConsentByRequestResponseJson.consent_request_id.head should be(consentRequestId)
getConsentByRequestResponseJson.status should be(ConsentStatus.ACCEPTED.toString)
@ -280,7 +280,7 @@ class VRPConsentRequestTest extends V510ServerSetup with PropsReset{
val getConsentByRequestResponse = makeGetRequest(getConsentByRequestIdUrl(consentRequestId))
Then("We should get a 200")
getConsentByRequestResponse.code should equal(200)
val getConsentByRequestResponseJson = getConsentByRequestResponse.body.extract[ConsentJsonV510]
val getConsentByRequestResponseJson = getConsentByRequestResponse.body.extract[ConsentJsonV500]
getConsentByRequestResponseJson.consent_request_id.head should be(consentRequestId)
getConsentByRequestResponseJson.status should be(ConsentStatus.ACCEPTED.toString)
}

View File

@ -3,6 +3,7 @@
### Most recent changes at top of file
```
Date Commit Action
24/01/2025 ad68f054 Added props skip_consent_sca_for_consumer_id_pairs .
03/02/2024 7bcb6bc5 Added props oauth2.keycloak.source_of_truth, default is false.
oauth2.keycloak.source_of_truth = true turns sync ON.
It is used to sync IAM of OBP-API and IAM of Keycloak.