mirror of
https://github.com/OpenBankProject/OBP-API.git
synced 2026-02-06 18:46:46 +00:00
Merge pull request #2296 from hongwei1/develop
feature/use Twilio as OBP OTP library
This commit is contained in:
commit
66ff69184c
@ -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 --------------------------
|
||||
|
||||
@ -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
|
||||
@ -1057,7 +1056,6 @@ object ToSchemify {
|
||||
MethodRouting,
|
||||
EndpointMapping,
|
||||
WebUiProps,
|
||||
Authorisation,
|
||||
DynamicEntity,
|
||||
DynamicData,
|
||||
DynamicEndpoint,
|
||||
|
||||
@ -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, _}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
@ -334,14 +333,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)
|
||||
}
|
||||
@ -396,12 +397,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)
|
||||
|
||||
@ -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]
|
||||
}
|
||||
//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]
|
||||
//}
|
||||
@ -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)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
Loading…
Reference in New Issue
Block a user