feature/added props skip_consent_sca_for_consumer_id_pairs

This commit is contained in:
hongwei 2025-01-24 22:10:59 +08:00
parent 1beab56f5a
commit ad68f054fb
4 changed files with 228 additions and 150 deletions

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,
@ -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

@ -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
@ -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

@ -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))
}
}
}