From 5e54176c9e9d48da77bfe7473da6420a67cd9863 Mon Sep 17 00:00:00 2001 From: hongwei Date: Tue, 17 Oct 2023 17:22:35 +0200 Subject: [PATCH 1/3] feature/use Twilio as OBP OTP library --- .../resources/props/sample.props.template | 7 ++-- .../bankconnectors/LocalMappedConnector.scala | 39 +++++++++++++------ 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/obp-api/src/main/resources/props/sample.props.template b/obp-api/src/main/resources/props/sample.props.template index f37b8326d3..93d59e399b 100644 --- a/obp-api/src/main/resources/props/sample.props.template +++ b/obp-api/src/main/resources/props/sample.props.template @@ -887,9 +887,10 @@ database_messages_scheduler_interval=3600 # --------------------------------------------------------- # -- SCA (Strong Customer Authentication) ------- -# For now, OBP-API use `Twilio` server as the SMS provider. Please check `Twilio` website, and get the api key and value there. -# sca_phone_api_key = oXAjqAJ6rvCunpzN -# sca_phone_api_secret =oXAjqAJ6rvCunpzN123sdf +# For now, OBP-API use `Twilio` server as the SMS provider. Please check `Twilio` website, and get the api key, value and phone number there. +# sca_phone_api_key = ACobpb72ab850501b5obp8dobp9dobp111 +# sca_phone_api_secret =7afobpdacobpd427obpff87a22obp222 +# sca_phone_api_id =MGcobp8575119887f10b62a2461obpb333 # # -- PSD2 Certificates -------------------------- diff --git a/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala b/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala index faad8669eb..d6a577d4f0 100644 --- a/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala +++ b/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala @@ -329,14 +329,16 @@ object LocalMappedConnector extends Connector with MdcLoggable { challengeId.toList } - Authorisations.authorisationProvider.vend.createAuthorization( - transactionRequestId.getOrElse(""), - consentId.getOrElse(""), - AuthenticationType.SMS_OTP.toString, - "", - ScaStatus.received.toString, - "12345" // TODO Implement SMS sending - ) + //We use obp MappedExpectedChallengeAnswer instead of Authorisations now. + // please also check Challenges.ChallengeProvider.vend.saveChallenge +// Authorisations.authorisationProvider.vend.createAuthorization( +// transactionRequestId.getOrElse(""), +// consentId.getOrElse(""), +// AuthenticationType.SMS_OTP.toString, +// "", +// ScaStatus.received.toString, +// "12345" // TODO Implement SMS sending +// ) (Full(challenges.flatten), callContext) } @@ -391,12 +393,25 @@ object LocalMappedConnector extends Connector with MdcLoggable { for { smsProviderApiKey <- APIUtil.getPropsValue("sca_phone_api_key") ?~! s"$MissingPropsValueAtThisInstance sca_phone_api_key" smsProviderApiSecret <- APIUtil.getPropsValue("sca_phone_api_secret") ?~! s"$MissingPropsValueAtThisInstance sca_phone_api_secret" - client = Twilio.init(smsProviderApiKey, smsProviderApiSecret) + scaPhoneApiId <- APIUtil.getPropsValue("sca_phone_api_id") ?~! s"$MissingPropsValueAtThisInstance sca_phone_api_id" + client = Twilio.init(smsProviderApiKey, smsProviderApiSecret) //TODO, move this to other place, we only need to init it once. phoneNumber = tuple._2 messageText = s"Your consent challenge : ${challengeAnswer}"; - message: Box[Message] = tryo(Message.creator(new PhoneNumber(phoneNumber), new PhoneNumber(phoneNumber), messageText).create()) - failMsg = s"$SmsServerNotResponding: $phoneNumber. Or Please to use EMAIL first. ${message.map(_.getErrorMessage).getOrElse("")}" - _ <- Helper.booleanToBox(message.forall(_.getErrorMessage.isEmpty), failMsg) + message: Message <- tryo {Message.creator( + new PhoneNumber(phoneNumber), + scaPhoneApiId, + messageText).create()} + + isSuccess <- tryo {message.getErrorMessage == null} + + _ = logger.debug(s"createChallengeInternal.send message to $phoneNumber, detail is $message") + + failMsg = if (message.getErrorMessage ==null) + s"$SmsServerNotResponding: $phoneNumber. Or Please to use EMAIL first. ${message.getErrorMessage}" + else + s"$SmsServerNotResponding: $phoneNumber. Or Please to use EMAIL first." + + _ <- Helper.booleanToBox(isSuccess, failMsg) } yield true } val errorMessage = sendingResult.filter(_.isInstanceOf[Failure]).map(_.asInstanceOf[Failure].msg) From a05d13e8624f01fbe5449985ddcee121c6613513 Mon Sep 17 00:00:00 2001 From: hongwei Date: Wed, 18 Oct 2023 12:40:42 +0200 Subject: [PATCH 2/3] refactor/commented the whole Authorisation --- .../main/scala/bootstrap/liftweb/Boot.scala | 2 - .../v1_3/JSONFactory_BERLIN_GROUP_1_3.scala | 1 - .../bankconnectors/LocalMappedConnector.scala | 1 - .../authorisation/Authorisation.scala | 50 ++--- .../authorisation/MapperAuthorisation.scala | 194 +++++++++--------- 5 files changed, 122 insertions(+), 126 deletions(-) diff --git a/obp-api/src/main/scala/bootstrap/liftweb/Boot.scala b/obp-api/src/main/scala/bootstrap/liftweb/Boot.scala index c550326e8b..18ae2307d8 100644 --- a/obp-api/src/main/scala/bootstrap/liftweb/Boot.scala +++ b/obp-api/src/main/scala/bootstrap/liftweb/Boot.scala @@ -70,7 +70,6 @@ import code.customer.{MappedCustomer, MappedCustomerMessage} import code.customeraccountlinks.CustomerAccountLink import code.customeraddress.MappedCustomerAddress import code.customerattribute.MappedCustomerAttribute -import code.database.authorisation.Authorisation import code.directdebit.DirectDebit import code.dynamicEntity.DynamicEntity import code.dynamicMessageDoc.DynamicMessageDoc @@ -1061,7 +1060,6 @@ object ToSchemify { MethodRouting, EndpointMapping, WebUiProps, - Authorisation, DynamicEntity, DynamicData, DynamicEndpoint, diff --git a/obp-api/src/main/scala/code/api/berlin/group/v1_3/JSONFactory_BERLIN_GROUP_1_3.scala b/obp-api/src/main/scala/code/api/berlin/group/v1_3/JSONFactory_BERLIN_GROUP_1_3.scala index 9c660b5bf4..c71f08da15 100644 --- a/obp-api/src/main/scala/code/api/berlin/group/v1_3/JSONFactory_BERLIN_GROUP_1_3.scala +++ b/obp-api/src/main/scala/code/api/berlin/group/v1_3/JSONFactory_BERLIN_GROUP_1_3.scala @@ -7,7 +7,6 @@ import code.api.util.APIUtil._ import code.api.util.{APIUtil, ConsentJWT, CustomJsonFormats, JwtUtil} import code.bankconnectors.Connector import code.consent.ConsentTrait -import code.database.authorisation.Authorisation import code.model.ModeratedTransaction import com.openbankproject.commons.model.enums.AccountRoutingScheme import com.openbankproject.commons.model.{BankAccount, TransactionRequest, User, _} diff --git a/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala b/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala index d6a577d4f0..9291d82a52 100644 --- a/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala +++ b/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala @@ -34,7 +34,6 @@ import code.customer._ import code.customeraccountlinks.CustomerAccountLinkTrait import code.customeraddress.CustomerAddressX import code.customerattribute.CustomerAttributeX -import code.database.authorisation.Authorisations import code.directdebit.DirectDebits import code.endpointTag.{EndpointTag, EndpointTagT} import code.fx.fx.TTL diff --git a/obp-api/src/main/scala/code/database/authorisation/Authorisation.scala b/obp-api/src/main/scala/code/database/authorisation/Authorisation.scala index f5fcaf17b4..1f0ff89bf0 100644 --- a/obp-api/src/main/scala/code/database/authorisation/Authorisation.scala +++ b/obp-api/src/main/scala/code/database/authorisation/Authorisation.scala @@ -1,25 +1,25 @@ -package code.database.authorisation - -import net.liftweb.common.Box -import net.liftweb.util.SimpleInjector - - -object Authorisations extends SimpleInjector { - val authorisationProvider = new Inject(buildOne _) {} - def buildOne: AuthorisationProvider = MappedAuthorisationProvider -} - -trait AuthorisationProvider { - def getAuthorizationByAuthorizationId(authorizationId: String): Box[Authorisation] - def getAuthorizationByAuthorizationId(paymentId: String, authorizationId: String): Box[Authorisation] - def getAuthorizationByPaymentId(paymentId: String): Box[List[Authorisation]] - def getAuthorizationByConsentId(consentId: String): Box[List[Authorisation]] - def createAuthorization(paymentId: String, - consentId: String, - authenticationType: String, - authenticationMethodId: String, - scaStatus: String, - challengeData: String - ): Box[Authorisation] - def checkAnswer(paymentId: String, authorizationId: String, challengeData: String): Box[Authorisation] -} \ No newline at end of file +//package code.database.authorisation +// +//import net.liftweb.common.Box +//import net.liftweb.util.SimpleInjector +// +// +//object Authorisations extends SimpleInjector { +// val authorisationProvider = new Inject(buildOne _) {} +// def buildOne: AuthorisationProvider = MappedAuthorisationProvider +//} +// +//trait AuthorisationProvider { +// def getAuthorizationByAuthorizationId(authorizationId: String): Box[Authorisation] +// def getAuthorizationByAuthorizationId(paymentId: String, authorizationId: String): Box[Authorisation] +// def getAuthorizationByPaymentId(paymentId: String): Box[List[Authorisation]] +// def getAuthorizationByConsentId(consentId: String): Box[List[Authorisation]] +// def createAuthorization(paymentId: String, +// consentId: String, +// authenticationType: String, +// authenticationMethodId: String, +// scaStatus: String, +// challengeData: String +// ): Box[Authorisation] +// def checkAnswer(paymentId: String, authorizationId: String, challengeData: String): Box[Authorisation] +//} \ No newline at end of file diff --git a/obp-api/src/main/scala/code/database/authorisation/MapperAuthorisation.scala b/obp-api/src/main/scala/code/database/authorisation/MapperAuthorisation.scala index 834ce8a012..670d658038 100644 --- a/obp-api/src/main/scala/code/database/authorisation/MapperAuthorisation.scala +++ b/obp-api/src/main/scala/code/database/authorisation/MapperAuthorisation.scala @@ -1,97 +1,97 @@ -package code.database.authorisation - -import code.api.BerlinGroup.ScaStatus -import code.api.util.ErrorMessages -import code.consent.{ConsentStatus, MappedConsent} -import code.util.MappedUUID -import net.liftweb.common.{Box, Empty, Failure, Full} -import net.liftweb.mapper.{BaseIndex, By, CreatedUpdated, IdPK, LongKeyedMapper, LongKeyedMetaMapper, MappedString, UniqueIndex} -import net.liftweb.util.Helpers.tryo - - -class Authorisation extends LongKeyedMapper[Authorisation] with IdPK with CreatedUpdated { - def getSingleton = Authorisation - // Enum: received, psuIdentified, psuAuthenticated, scaMethodSelected, started, finalised, failed, exempted - object ScaStatus extends MappedString(this, 20) - object AuthorisationId extends MappedUUID(this) - object PaymentId extends MappedUUID(this) - object ConsentId extends MappedUUID(this) - // Enum: SMS_OTP, CHIP_OTP, PHOTO_OTP, PUSH_OTP - object AuthenticationType extends MappedString(this, 10) - object AuthenticationMethodId extends MappedString(this, 35) - object ChallengeData extends MappedString(this, 1024) - - def scaStatus: String = ScaStatus.get - def authorisationId: String = AuthorisationId.get - def paymentId: String = PaymentId.get - def consentId: String = ConsentId.get - def authenticationType: String = AuthenticationType.get - def authenticationMethodId: String = AuthenticationMethodId.get - def challengeData: String = ChallengeData.get -} - -object Authorisation extends Authorisation with LongKeyedMetaMapper[Authorisation] { - override def dbIndexes: List[BaseIndex[Authorisation]] = UniqueIndex(AuthorisationId) :: super.dbIndexes -} - -object MappedAuthorisationProvider extends AuthorisationProvider { - override def getAuthorizationByAuthorizationId(paymentId: String, authorizationId: String): Box[Authorisation] = { - val result: Box[Authorisation] = Authorisation.find( - By(Authorisation.PaymentId, paymentId), - By(Authorisation.AuthorisationId, authorizationId) - ) - result - } - override def getAuthorizationByAuthorizationId(authorizationId: String): Box[Authorisation] = { - val result: Box[Authorisation] = Authorisation.find( - By(Authorisation.AuthorisationId, authorizationId) - ) - result - } - - override def getAuthorizationByPaymentId(paymentId: String): Box[List[Authorisation]] = { - tryo(Authorisation.findAll(By(Authorisation.PaymentId, paymentId))) - } - override def getAuthorizationByConsentId(consentId: String): Box[List[Authorisation]] = { - tryo(Authorisation.findAll(By(Authorisation.ConsentId, consentId))) - } - - def createAuthorization(paymentId: String, - consentId: String, - authenticationType: String, - authenticationMethodId: String, - scaStatus: String, - challengeData: String - ): Box[Authorisation] = tryo { - Authorisation - .create - .PaymentId(paymentId) - .ConsentId(consentId) - .AuthenticationType(authenticationType) - .AuthenticationMethodId(authenticationMethodId) - .ChallengeData(challengeData) - .ScaStatus(scaStatus).saveMe() - } - - def checkAnswer(paymentId: String, authorizationId: String, challengeData: String): Box[Authorisation] = - getAuthorizationByAuthorizationId(paymentId: String, authorizationId: String) match { - case Full(authorisation) => - authorisation.scaStatus match { - case value if value == ScaStatus.received.toString => - val status = if (authorisation.challengeData == challengeData) ScaStatus.finalised.toString else ScaStatus.failed.toString - tryo(authorisation.ScaStatus(status).saveMe()) - case _ => //make sure, only `received` can be processed, all others are invalid . - Failure(s"${ErrorMessages.InvalidAuthorisationStatus}.It should be `received`, but now it is `${authorisation.scaStatus}`") - } - case Empty => - Empty ?~! s"${ErrorMessages.AuthorisationNotFound} Current PAYMENT_ID($paymentId) and AUTHORISATION_ID ($authorizationId)," - case Failure(msg, _, _) => - Failure(msg) - case _ => - Failure(ErrorMessages.UnknownError) - } -} - - - - +//package code.database.authorisation +// +//import code.api.BerlinGroup.ScaStatus +//import code.api.util.ErrorMessages +//import code.consent.{ConsentStatus, MappedConsent} +//import code.util.MappedUUID +//import net.liftweb.common.{Box, Empty, Failure, Full} +//import net.liftweb.mapper.{BaseIndex, By, CreatedUpdated, IdPK, LongKeyedMapper, LongKeyedMetaMapper, MappedString, UniqueIndex} +//import net.liftweb.util.Helpers.tryo +// +// +//class Authorisation extends LongKeyedMapper[Authorisation] with IdPK with CreatedUpdated { +// def getSingleton = Authorisation +// // Enum: received, psuIdentified, psuAuthenticated, scaMethodSelected, started, finalised, failed, exempted +// object ScaStatus extends MappedString(this, 20) +// object AuthorisationId extends MappedUUID(this) +// object PaymentId extends MappedUUID(this) +// object ConsentId extends MappedUUID(this) +// // Enum: SMS_OTP, CHIP_OTP, PHOTO_OTP, PUSH_OTP +// object AuthenticationType extends MappedString(this, 10) +// object AuthenticationMethodId extends MappedString(this, 35) +// object ChallengeData extends MappedString(this, 1024) +// +// def scaStatus: String = ScaStatus.get +// def authorisationId: String = AuthorisationId.get +// def paymentId: String = PaymentId.get +// def consentId: String = ConsentId.get +// def authenticationType: String = AuthenticationType.get +// def authenticationMethodId: String = AuthenticationMethodId.get +// def challengeData: String = ChallengeData.get +//} +// +//object Authorisation extends Authorisation with LongKeyedMetaMapper[Authorisation] { +// override def dbIndexes: List[BaseIndex[Authorisation]] = UniqueIndex(AuthorisationId) :: super.dbIndexes +//} +// +//object MappedAuthorisationProvider extends AuthorisationProvider { +// override def getAuthorizationByAuthorizationId(paymentId: String, authorizationId: String): Box[Authorisation] = { +// val result: Box[Authorisation] = Authorisation.find( +// By(Authorisation.PaymentId, paymentId), +// By(Authorisation.AuthorisationId, authorizationId) +// ) +// result +// } +// override def getAuthorizationByAuthorizationId(authorizationId: String): Box[Authorisation] = { +// val result: Box[Authorisation] = Authorisation.find( +// By(Authorisation.AuthorisationId, authorizationId) +// ) +// result +// } +// +// override def getAuthorizationByPaymentId(paymentId: String): Box[List[Authorisation]] = { +// tryo(Authorisation.findAll(By(Authorisation.PaymentId, paymentId))) +// } +// override def getAuthorizationByConsentId(consentId: String): Box[List[Authorisation]] = { +// tryo(Authorisation.findAll(By(Authorisation.ConsentId, consentId))) +// } +// +// def createAuthorization(paymentId: String, +// consentId: String, +// authenticationType: String, +// authenticationMethodId: String, +// scaStatus: String, +// challengeData: String +// ): Box[Authorisation] = tryo { +// Authorisation +// .create +// .PaymentId(paymentId) +// .ConsentId(consentId) +// .AuthenticationType(authenticationType) +// .AuthenticationMethodId(authenticationMethodId) +// .ChallengeData(challengeData) +// .ScaStatus(scaStatus).saveMe() +// } +// +// def checkAnswer(paymentId: String, authorizationId: String, challengeData: String): Box[Authorisation] = +// getAuthorizationByAuthorizationId(paymentId: String, authorizationId: String) match { +// case Full(authorisation) => +// authorisation.scaStatus match { +// case value if value == ScaStatus.received.toString => +// val status = if (authorisation.challengeData == challengeData) ScaStatus.finalised.toString else ScaStatus.failed.toString +// tryo(authorisation.ScaStatus(status).saveMe()) +// case _ => //make sure, only `received` can be processed, all others are invalid . +// Failure(s"${ErrorMessages.InvalidAuthorisationStatus}.It should be `received`, but now it is `${authorisation.scaStatus}`") +// } +// case Empty => +// Empty ?~! s"${ErrorMessages.AuthorisationNotFound} Current PAYMENT_ID($paymentId) and AUTHORISATION_ID ($authorizationId)," +// case Failure(msg, _, _) => +// Failure(msg) +// case _ => +// Failure(ErrorMessages.UnknownError) +// } +//} +// +// +// +// From 5e18b1268eeaecd086ec7896395aee4d4c0fa0ad Mon Sep 17 00:00:00 2001 From: hongwei Date: Thu, 19 Oct 2023 10:51:20 +0200 Subject: [PATCH 3/3] refactor/added the checkShortString to createBank endpoint --- .../src/main/scala/code/api/v2_2_0/APIMethods220.scala | 5 +++++ .../src/main/scala/code/api/v4_0_0/APIMethods400.scala | 8 +++++++- .../src/main/scala/code/api/v5_0_0/APIMethods500.scala | 9 ++++++++- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/obp-api/src/main/scala/code/api/v2_2_0/APIMethods220.scala b/obp-api/src/main/scala/code/api/v2_2_0/APIMethods220.scala index 614a5d14cf..f35fc42238 100644 --- a/obp-api/src/main/scala/code/api/v2_2_0/APIMethods220.scala +++ b/obp-api/src/main/scala/code/api/v2_2_0/APIMethods220.scala @@ -493,6 +493,11 @@ trait APIMethods220 { bank <- tryo{ json.extract[BankJSONV220] } ?~! ErrorMessages.InvalidJsonFormat _ <- Helper.booleanToBox( bank.id.length > 5,s"$InvalidJsonFormat Min length of BANK_ID should be 5 characters.") + + checkShortStringValue = APIUtil.checkShortString(bank.id) + + _ <- Helper.booleanToBox(checkShortStringValue == SILENCE_IS_GOLDEN, s"$checkShortStringValue.") + _ <- Helper.booleanToBox( !`checkIfContains::::` (bank.id), s"$InvalidJsonFormat BANK_ID can not contain `::::` characters") u <- cc.user ?~!ErrorMessages.UserNotLoggedIn 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 47ef589523..33dfd55863 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 @@ -72,7 +72,7 @@ import code.transactionrequests.TransactionRequests.TransactionRequestTypes.{app import code.usercustomerlinks.UserCustomerLink import code.userlocks.UserLocksProvider import code.users.Users -import code.util.Helper.{ObpS, booleanToFuture} +import code.util.Helper.{ObpS, SILENCE_IS_GOLDEN, booleanToFuture} import code.util.{Helper, JsonSchemaUtil} import code.validation.JsonValidation import code.views.Views @@ -4039,6 +4039,12 @@ trait APIMethods400 { cc.callContext.map(_.consumer.isDefined == true).isDefined } + checkShortStringValue = APIUtil.checkShortString(bank.id) + + _ <- Helper.booleanToFuture(failMsg = s"$checkShortStringValue.", cc=cc.callContext) { + checkShortStringValue==SILENCE_IS_GOLDEN + } + _ <- Helper.booleanToFuture(failMsg = s"$InvalidJsonFormat Min length of BANK_ID should be greater than 3 characters.", cc=cc.callContext) { bank.id.length > 3 } diff --git a/obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala b/obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala index 2ff305c850..c9688837bb 100644 --- a/obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala +++ b/obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala @@ -26,7 +26,7 @@ import code.model._ import code.model.dataAccess.BankAccountCreation import code.transactionrequests.TransactionRequests.TransactionRequestTypes.{apply => _} import code.util.Helper -import code.util.Helper.booleanToFuture +import code.util.Helper.{SILENCE_IS_GOLDEN, booleanToFuture} import code.views.Views import com.github.dwickern.macros.NameOf.nameOf import com.openbankproject.commons.ExecutionContext.Implicits.global @@ -183,6 +183,13 @@ trait APIMethods500 { postJson <- NewStyle.function.tryons(failMsg, 400, cc.callContext) { json.extract[PostBankJson500] } + + //if postJson.id is empty, just return SILENCE_IS_GOLDEN, and will pass the guard. + checkShortStringValue = APIUtil.checkShortString(postJson.id.getOrElse(SILENCE_IS_GOLDEN)) + _ <- Helper.booleanToFuture(failMsg = s"$checkShortStringValue.", cc = cc.callContext) { + checkShortStringValue == SILENCE_IS_GOLDEN + } + _ <- Helper.booleanToFuture(failMsg = ErrorMessages.InvalidConsumerCredentials, cc=cc.callContext) { cc.callContext.map(_.consumer.isDefined == true).isDefined }