diff --git a/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/MessageDocsSwaggerDefinitions.scala b/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/MessageDocsSwaggerDefinitions.scala index 6fbd7001e4..57f2e9c60c 100644 --- a/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/MessageDocsSwaggerDefinitions.scala +++ b/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/MessageDocsSwaggerDefinitions.scala @@ -79,13 +79,44 @@ object MessageDocsSwaggerDefinitions emailAddress = emailExample.value, name = usernameExample.value ))))))) + + val outboundAdapterConsenterInfo = OutboundAdapterAuthInfo( + userId = Some(userIdExample.value), + username = Some(usernameExample.value), + linkedCustomers = Some(List(BasicLinkedCustomer(customerIdExample.value,customerNumberExample.value,legalNameExample.value))), + userAuthContext = Some(List(BasicUserAuthContext(keyExample.value,valueExample.value))), //be set by obp from some endpoints. + authViews = Some(List(AuthView( + view = ViewBasic( + id = viewIdExample.value, + name = viewNameExample.value, + description = viewDescriptionExample.value, + ), + account = AccountBasic( + id = accountIdExample.value, + accountRoutings =List(AccountRouting( + scheme = accountRoutingSchemeExample.value, + address = accountRoutingAddressExample.value + )), + customerOwners = List(InternalBasicCustomer( + bankId = bankIdExample.value, + customerId = customerIdExample.value, + customerNumber = customerNumberExample.value, + legalName = legalNameExample.value, + dateOfBirth=parseDate(dateOfBirthExample.value).getOrElse(sys.error("dateOfBirthExample.value is not validate date format.")), + )), + userOwners = List(InternalBasicUser( + userId = userIdExample.value, + emailAddress = emailExample.value, + name = usernameExample.value + ))))))) val outboundAdapterCallContext = OutboundAdapterCallContext( correlationIdExample.value, Some(sessionIdExample.value), Some(consumerIdExample.value), generalContext = Some(List(BasicGeneralContext(keyExample.value,valueExample.value))), - Some(outboundAdapterAuthInfo) + Some(outboundAdapterAuthInfo), + Some(outboundAdapterConsenterInfo) ) val inboundAdapterCallContext = InboundAdapterCallContext( diff --git a/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala b/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala index 894194da38..680e052e36 100644 --- a/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala +++ b/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala @@ -5469,13 +5469,14 @@ object SwaggerDefinitionsJSON { ) val consentRequestToAccountJson = ConsentRequestToAccountJson ( + counterparty_name = counterpartyNameExample.value, bank_routing = bankRoutingJsonV121, account_routing = accountRoutingJsonV121, branch_routing = branchRoutingJsonV141, limit = postCounterpartyLimitV510 ) - val postConsentRequestJsonV510 = PostConsentRequestJsonV510( + val postVRPConsentRequestJsonV510 = PostVRPConsentRequestJsonV510( from_account = consentRequestFromAccountJson, to_account = consentRequestToAccountJson, email = Some(emailExample.value), diff --git a/obp-api/src/main/scala/code/api/berlin/group/v1_3/AccountInformationServiceAISApi.scala b/obp-api/src/main/scala/code/api/berlin/group/v1_3/AccountInformationServiceAISApi.scala index beeabff9d3..cfde8412ed 100644 --- a/obp-api/src/main/scala/code/api/berlin/group/v1_3/AccountInformationServiceAISApi.scala +++ b/obp-api/src/main/scala/code/api/berlin/group/v1_3/AccountInformationServiceAISApi.scala @@ -75,7 +75,7 @@ object APIMethods_AccountInformationServiceAISApi extends RestHelper { Future { Helper.booleanToBox(u.hasViewAccess(BankIdAccountId(account.bankId, account.accountId), viewId, callContext)) } map { - unboxFullOrFail(_, callContext, NoViewReadAccountsBerlinGroup + " userId : " + u.userId + ". account : " + account.accountId, 403) + unboxFullOrFail(_, callContext, s"$NoViewReadAccountsBerlinGroup ${viewId.value} userId : ${u.userId}. account : ${account.accountId}", 403) } } @@ -186,7 +186,8 @@ As a last option, an ASPSP might in addition accept a command with access rights createdConsent.secret, createdConsent.consentId, callContext.flatMap(_.consumer).map(_.consumerId.get), - Some(validUntil) + Some(validUntil), + callContext ) map { i => connectorEmptyResponse(i, callContext) } @@ -1257,15 +1258,12 @@ Maybe in a later version the access path will change. updateJson <- NewStyle.function.tryons(failMsg, 400, callContext) { jsonPut.extract[TransactionAuthorisation] } - (challenges, callContext) <- NewStyle.function.getChallengesByConsentId(consentId, callContext) - _ <- NewStyle.function.tryons(s"$AuthorisationNotFound Current AUTHORISATION_ID($authorisationId)", 400, callContext) { - challenges.filter(_.challengeId == authorisationId).size == 1 - } + (_, callContext) <- NewStyle.function.getChallenge(authorisationId, callContext) (challenge, callContext) <- NewStyle.function.validateChallengeAnswerC4( ChallengeType.BERLIN_GROUP_CONSENT_CHALLENGE, None, Some(consentId), - challenges.filter(_.challengeId == authorisationId).head.challengeId, + authorisationId, updateJson.scaAuthenticationData, SuppliedAnswerType.PLAIN_TEXT_VALUE, callContext diff --git a/obp-api/src/main/scala/code/api/berlin/group/v1_3/PaymentInitiationServicePISApi.scala b/obp-api/src/main/scala/code/api/berlin/group/v1_3/PaymentInitiationServicePISApi.scala index 6f9e369a83..a953825243 100644 --- a/obp-api/src/main/scala/code/api/berlin/group/v1_3/PaymentInitiationServicePISApi.scala +++ b/obp-api/src/main/scala/code/api/berlin/group/v1_3/PaymentInitiationServicePISApi.scala @@ -930,7 +930,7 @@ This applies in the following scenarios: None, callContext ) - //NOTE: in OBP it support multiple challenges, but in Berlin Group it has only one challenge. The following guard is to make sure it return the 1st challenge properly. + //NOTE: in OBP it support multiple challenges, but in Berlin Group it has only one challenge. The following guard is to make sure it returns the 1st challenge properly. challenge <- NewStyle.function.tryons(InvalidConnectorResponseForCreateChallenge, 400, callContext) { challenges.head } @@ -1444,7 +1444,7 @@ There are the following request types on this access path: ) lazy val updatePaymentPsuDataTransactionAuthorisation : OBPEndpoint = { - case paymentService :: paymentProduct :: paymentId:: "authorisations" :: authorisationid :: Nil JsonPut json -> _ if checkTransactionAuthorisation(json) => { + case paymentService :: paymentProduct :: paymentId:: "authorisations" :: authorisationId :: Nil JsonPut json -> _ if checkTransactionAuthorisation(json) => { cc => for { (Full(u), callContext) <- authenticatedAccess(cc) @@ -1466,11 +1466,12 @@ There are the following request types on this access path: _ <- Helper.booleanToFuture(failMsg= CannotUpdatePSUData, cc=callContext) { existingTransactionRequest.status == TransactionRequestStatus.INITIATED.toString } + (_, callContext) <- NewStyle.function.getChallenge(authorisationId, callContext) (challenge, callContext) <- NewStyle.function.validateChallengeAnswerC4( ChallengeType.BERLIN_GROUP_PAYMENT_CHALLENGE, Some(paymentId), None, - authorisationid, + authorisationId, transactionAuthorisationJson.scaAuthenticationData, SuppliedAnswerType.PLAIN_TEXT_VALUE, callContext diff --git a/obp-api/src/main/scala/code/api/berlin/group/v1_3/SigningBasketsApi.scala b/obp-api/src/main/scala/code/api/berlin/group/v1_3/SigningBasketsApi.scala index 337484981d..50464a1809 100644 --- a/obp-api/src/main/scala/code/api/berlin/group/v1_3/SigningBasketsApi.scala +++ b/obp-api/src/main/scala/code/api/berlin/group/v1_3/SigningBasketsApi.scala @@ -111,7 +111,7 @@ The resource identifications of these transactions are contained in the payload jsonPost.extract[PostSigningBasketJsonV13] } _ <- booleanToFuture(failMsg, cc = callContext) { - // One of them MUST be defined. Otherwise post json is treated as empty one. + // One of them MUST be defined. Otherwise, post json is treated as empty one. !(jsonPost.extract[PostSigningBasketJsonV13].paymentIds.isEmpty && jsonPost.extract[PostSigningBasketJsonV13].consentIds.isEmpty) } diff --git a/obp-api/src/main/scala/code/api/util/APIUtil.scala b/obp-api/src/main/scala/code/api/util/APIUtil.scala index 74f57dfa9e..2f16c2c2d0 100644 --- a/obp-api/src/main/scala/code/api/util/APIUtil.scala +++ b/obp-api/src/main/scala/code/api/util/APIUtil.scala @@ -3649,11 +3649,13 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{ lazy val canAddTransactionRequestToAnyAccount = view.map(_.canAddTransactionRequestToAnyAccount).getOrElse(false) + lazy val canAddTransactionRequestToBeneficiary = view.map(_.canAddTransactionRequestToBeneficiary).getOrElse(false) //1st check the admin level role/entitlement `canCreateAnyTransactionRequest` if (hasCanCreateAnyTransactionRequestRole) { Full(true) - //2rd: check if the user have the view access and the view has the `canAddTransactionRequestToAnyAccount` permission - } else if (canAddTransactionRequestToAnyAccount) { + } else if (canAddTransactionRequestToAnyAccount) { //2rd: check if the user have the view access and the view has the `canAddTransactionRequestToAnyAccount` permission + Full(true) + } else if (canAddTransactionRequestToBeneficiary) { //3erd: check if the user have the view access and the view has the `canAddTransactionRequestToBeneficiary` permission Full(true) } else { Empty diff --git a/obp-api/src/main/scala/code/api/util/ApiSession.scala b/obp-api/src/main/scala/code/api/util/ApiSession.scala index 2dd1c53783..e4aa24e911 100644 --- a/obp-api/src/main/scala/code/api/util/ApiSession.scala +++ b/obp-api/src/main/scala/code/api/util/ApiSession.scala @@ -30,6 +30,7 @@ case class CallContext( dauthResponseHeader: Option[String] = None, spelling: Option[String] = None, user: Box[User] = Empty, + consenter: Box[User] = Empty, consumer: Box[Consumer] = Empty, ipAddress: String = "", resourceDocument: Option[ResourceDoc] = None, @@ -96,7 +97,14 @@ case class CallContext( username = username, linkedCustomers = likedCustomersBasic, userAuthContext = basicUserAuthContexts, - if (authViews.isEmpty) None else Some(authViews))) + if (authViews.isEmpty) None else Some(authViews))), + outboundAdapterConsenterInfo = + if (this.consenter.isDefined){ + Some(OutboundAdapterAuthInfo( + username = this.consenter.toOption.map(_.name)))//TODO, here we may added more field to the consenter, at the moment only username is useful + }else{ + None + } ) }}.openOr(OutboundAdapterCallContext( //For anonymousAccess endpoints, there are no user info this.correlationId, diff --git a/obp-api/src/main/scala/code/api/util/ConsentUtil.scala b/obp-api/src/main/scala/code/api/util/ConsentUtil.scala index 1443568dfe..3b89e9da73 100644 --- a/obp-api/src/main/scala/code/api/util/ConsentUtil.scala +++ b/obp-api/src/main/scala/code/api/util/ConsentUtil.scala @@ -451,7 +451,7 @@ object Consent extends MdcLoggable { private def applyBerlinGroupConsentRulesCommon(consentId: String, callContext: CallContext): Future[(Box[User], Option[CallContext])] = { implicit val dateFormats = CustomJsonFormats.formats - def applyConsentRules(consent: ConsentJWT): Future[(Box[User], Option[CallContext])] = { + def applyConsentRules(consent: ConsentJWT, callContext: CallContext): Future[(Box[User], Option[CallContext])] = { val cc = callContext // 1. Get or Create a User getOrCreateUser(consent.sub, consent.iss, Some(consent.toConsent().consentId), None, None) map { @@ -507,7 +507,9 @@ object Consent extends MdcLoggable { case Full(storedConsent) => // Set Consumer into Call Context val consumer = getCurrentConsumerViaMtls(callContext) - val updatedCallContext = callContext.copy(consumer = consumer) + val user = Users.users.vend.getUserByUserId(storedConsent.userId) + logger.debug(s"applyBerlinGroupConsentRulesCommon.storedConsent.user : $user") + val updatedCallContext = callContext.copy(consumer = consumer).copy(consenter = user) // This function MUST be called only once per call. I.e. it's date dependent val (canBeUsed, currentCounterState) = checkFrequencyPerDay(storedConsent) if(canBeUsed) { @@ -524,7 +526,7 @@ object Consent extends MdcLoggable { // Update MappedConsent.usesSoFarTodayCounter field val consentUpdatedBox = Consents.consentProvider.vend.updateBerlinGroupConsent(consentId, currentCounterState + 1) logger.debug(s"applyBerlinGroupConsentRulesCommon.consentUpdatedBox: $consentUpdatedBox") - applyConsentRules(consent) + applyConsentRules(consent, updatedCallContext) case failure@Failure(_, _, _) => // Handled errors Future(failure, Some(updatedCallContext)) case _ => // Unexpected errors @@ -666,7 +668,8 @@ object Consent extends MdcLoggable { secret: String, consentId: String, consumerId: Option[String], - validUntil: Option[Date]): Future[Box[String]] = { + validUntil: Option[Date], + callContext: Option[CallContext]): Future[Box[String]] = { val currentTimeInSeconds = System.currentTimeMillis / 1000 val validUntilTimeInSeconds = validUntil match { @@ -682,7 +685,8 @@ object Consent extends MdcLoggable { // 1. Add access val accounts: List[Future[ConsentView]] = consent.access.accounts.getOrElse(Nil) map { account => - Connector.connector.vend.getBankAccountByIban(account.iban.getOrElse(""), None) map { bankAccount => + Connector.connector.vend.getBankAccountByIban(account.iban.getOrElse(""), callContext) map { bankAccount => + logger.debug(s"createBerlinGroupConsentJWT.accounts.bankAccount: $bankAccount") ConsentView( bank_id = bankAccount._1.map(_.bankId.value).getOrElse(""), account_id = bankAccount._1.map(_.accountId.value).getOrElse(""), @@ -691,7 +695,8 @@ object Consent extends MdcLoggable { } } val balances: List[Future[ConsentView]] = consent.access.balances.getOrElse(Nil) map { account => - Connector.connector.vend.getBankAccountByIban(account.iban.getOrElse(""), None) map { bankAccount => + Connector.connector.vend.getBankAccountByIban(account.iban.getOrElse(""), callContext) map { bankAccount => + logger.debug(s"createBerlinGroupConsentJWT.balances.bankAccount: $bankAccount") ConsentView( bank_id = bankAccount._1.map(_.bankId.value).getOrElse(""), account_id = bankAccount._1.map(_.accountId.value).getOrElse(""), @@ -700,7 +705,8 @@ object Consent extends MdcLoggable { } } val transactions: List[Future[ConsentView]] = consent.access.transactions.getOrElse(Nil) map { account => - Connector.connector.vend.getBankAccountByIban(account.iban.getOrElse(""), None) map { bankAccount => + Connector.connector.vend.getBankAccountByIban(account.iban.getOrElse(""), callContext) map { bankAccount => + logger.debug(s"createBerlinGroupConsentJWT.transactions.bankAccount: $bankAccount") ConsentView( bank_id = bankAccount._1.map(_.bankId.value).getOrElse(""), account_id = bankAccount._1.map(_.accountId.value).getOrElse(""), diff --git a/obp-api/src/main/scala/code/api/util/ErrorMessages.scala b/obp-api/src/main/scala/code/api/util/ErrorMessages.scala index 21733fe8ad..56149a0053 100644 --- a/obp-api/src/main/scala/code/api/util/ErrorMessages.scala +++ b/obp-api/src/main/scala/code/api/util/ErrorMessages.scala @@ -211,7 +211,7 @@ object ErrorMessages { val UserNotFoundByUserId = "OBP-20057: User not found by userId." val ConsumerIsDisabled = "OBP-20058: Consumer is disabled." val CouldNotGetUserLockStatus = "OBP-20059: Could not get the lock status of the user." - val NoViewReadAccountsBerlinGroup = s"OBP-20060: User does not have access to the view $SYSTEM_READ_ACCOUNTS_BERLIN_GROUP_VIEW_ID." + val NoViewReadAccountsBerlinGroup = s"OBP-20060: User does not have access to the view:" val FrequencyPerDayError = "OBP-20062: Frequency per day must be greater than 0." val FrequencyPerDayMustBeOneError = "OBP-20063: Frequency per day must be equal to 1 in case of one-off access." @@ -507,6 +507,7 @@ object ErrorMessages { val CounterpartyLimitAlreadyExists = "OBP-30264: Counterparty limit already exists. Please specify a different value for BANK_ID, ACCOUNT_ID, VIEW_ID or COUNTERPARTY_ID." val DeleteCounterpartyLimitError = "OBP-30265: Could not delete the counterparty limit." val CustomViewAlreadyExistsError = "OBP-30266: The custom view is already exists." + val UserDoesNotHavePermission = "OBP-30267: The user does not have the permission:" val TaxResidenceNotFound = "OBP-30300: Tax Residence not found by TAX_RESIDENCE_ID. " val CustomerAddressNotFound = "OBP-30310: Customer's Address not found by CUSTOMER_ADDRESS_ID. " @@ -599,7 +600,8 @@ object ErrorMessages { "because the login user doesn't have access to the view of the from account " + "or the consumer doesn't have the access to the view of the from account " + s"or the login user does not have the `${CanCreateAnyTransactionRequest.toString()}` role " + - s"or the view does not have the permission ${StringHelpers.snakify(ViewDefinition.canAddTransactionRequestToAnyAccount_.dbColumnName).dropRight(1)}." + s"or the view does not have the permission can_add_transaction_request_to_any_account " + + s"or the view does not have the permission can_add_transaction_request_to_beneficiary." val InvalidTransactionRequestCurrency = "OBP-40003: Transaction Request Currency must be the same as From Account Currency." val InvalidTransactionRequestId = "OBP-40004: Transaction Request Id not found." val InsufficientAuthorisationToCreateTransactionType = "OBP-40005: Insufficient authorisation to Create Transaction Type offered by the bank. The Request could not be created because you don't have access to CanCreateTransactionType." diff --git a/obp-api/src/main/scala/code/api/util/migration/Migration.scala b/obp-api/src/main/scala/code/api/util/migration/Migration.scala index 03c6f24c11..6309a8be44 100644 --- a/obp-api/src/main/scala/code/api/util/migration/Migration.scala +++ b/obp-api/src/main/scala/code/api/util/migration/Migration.scala @@ -101,6 +101,7 @@ object Migration extends MdcLoggable { alterMappedCustomerAttribute(startedBeforeSchemifier) dropMappedBadLoginAttemptIndex() alterMetricColumnUrlLength() + populateViewDefinitionCanAddTransactionRequestToBeneficiary() } private def dummyScript(): Boolean = { @@ -128,6 +129,13 @@ object Migration extends MdcLoggable { runOnce(name) { TableViewDefinition.populate(name) } + } + + private def populateViewDefinitionCanAddTransactionRequestToBeneficiary(): Boolean = { + val name = nameOf(populateViewDefinitionCanAddTransactionRequestToBeneficiary) + runOnce(name) { + MigrationOfViewDefinitionCanAddTransactionRequestToBeneficiary.populateTheField(name) + } } diff --git a/obp-api/src/main/scala/code/api/util/migration/MigrationOfViewDefinition.scala b/obp-api/src/main/scala/code/api/util/migration/MigrationOfViewDefinition.scala index 6771437630..a91c6d9970 100644 --- a/obp-api/src/main/scala/code/api/util/migration/MigrationOfViewDefinition.scala +++ b/obp-api/src/main/scala/code/api/util/migration/MigrationOfViewDefinition.scala @@ -115,6 +115,7 @@ object TableViewDefinition { .canSeeOtherAccountRoutingAddress_(view.canSeeOtherAccountRoutingAddress) .canAddTransactionRequestToOwnAccount_(view.canAddTransactionRequestToOwnAccount) .canAddTransactionRequestToAnyAccount_(view.canAddTransactionRequestToAnyAccount) + .canAddTransactionRequestToBeneficiary_(view.canAddTransactionRequestToBeneficiary) .save } val isSuccessful = insertedRows.forall(_ == true) diff --git a/obp-api/src/main/scala/code/api/util/migration/MigrationOfViewDefinitionCanAddTransactionRequestToBeneficiary.scala b/obp-api/src/main/scala/code/api/util/migration/MigrationOfViewDefinitionCanAddTransactionRequestToBeneficiary.scala new file mode 100644 index 0000000000..32fec4883d --- /dev/null +++ b/obp-api/src/main/scala/code/api/util/migration/MigrationOfViewDefinitionCanAddTransactionRequestToBeneficiary.scala @@ -0,0 +1,47 @@ +package code.api.util.migration + +import code.api.Constant.SYSTEM_OWNER_VIEW_ID + +import java.time.format.DateTimeFormatter +import java.time.{ZoneId, ZonedDateTime} +import code.api.util.APIUtil +import code.api.util.migration.Migration.{DbFunction, saveLog} +import code.model.Consumer +import code.views.system.ViewDefinition + +object MigrationOfViewDefinitionCanAddTransactionRequestToBeneficiary { + + val oneDayAgo = ZonedDateTime.now(ZoneId.of("UTC")).minusDays(1) + val oneYearInFuture = ZonedDateTime.now(ZoneId.of("UTC")).plusYears(1) + val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm'Z'") + + def populateTheField(name: String): Boolean = { + DbFunction.tableExists(ViewDefinition) match { + case true => + val startDate = System.currentTimeMillis() + val commitId: String = APIUtil.gitCommit + var isSuccessful = false + + val view = ViewDefinition.findSystemView(SYSTEM_OWNER_VIEW_ID).map(_.canAddTransactionRequestToBeneficiary_(true).saveMe()) + + + val endDate = System.currentTimeMillis() + val comment: String = + s"""set $SYSTEM_OWNER_VIEW_ID.canAddTransactionRequestToBeneficiary_ to {true}""".stripMargin + val value = view.map(_.canAddTransactionRequestToBeneficiary_.get).getOrElse(false) + isSuccessful = value + saveLog(name, commitId, isSuccessful, startDate, endDate, comment) + isSuccessful + + case false => + val startDate = System.currentTimeMillis() + val commitId: String = APIUtil.gitCommit + val isSuccessful = false + val endDate = System.currentTimeMillis() + val comment: String = + s"""${Consumer._dbTableNameLC} table does not exist""".stripMargin + saveLog(name, commitId, isSuccessful, startDate, endDate, comment) + isSuccessful + } + } +} diff --git a/obp-api/src/main/scala/code/api/util/newstyle/SigningBasketNewStyle.scala b/obp-api/src/main/scala/code/api/util/newstyle/SigningBasketNewStyle.scala index d0ce26305e..624f908239 100644 --- a/obp-api/src/main/scala/code/api/util/newstyle/SigningBasketNewStyle.scala +++ b/obp-api/src/main/scala/code/api/util/newstyle/SigningBasketNewStyle.scala @@ -23,7 +23,7 @@ object SigningBasketNewStyle { basket.flatMap(_.payments.map(_.forall(i => Connector.connector.vend.getTransactionRequestImpl(TransactionRequestId(i), callContext).isDefined))) if (existAll.getOrElse(false)) { Some(true) - } else { // Fail due to unexisting payment + } else { // Fail due to nonexistent payment val paymentIds = basket.flatMap(_.payments).getOrElse(Nil).mkString(",") unboxFullOrFail(Empty, callContext, s"$InvalidConnectorResponse Some of paymentIds [${paymentIds}] are invalid") } 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 7d5c5a7200..2ea62793f7 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 @@ -335,9 +335,9 @@ trait APIMethods220 { } resourceDocs += ResourceDoc( - getExplictCounterpartiesForAccount, + getExplicitCounterpartiesForAccount, implementedInApiVersion, - "getExplictCounterpartiesForAccount", + "getExplicitCounterpartiesForAccount", "GET", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/counterparties", "Get Counterparties (Explicit)", @@ -357,7 +357,7 @@ trait APIMethods220 { ), List(apiTagCounterparty, apiTagPSD2PIS, apiTagAccount, apiTagPsd2)) - lazy val getExplictCounterpartiesForAccount : OBPEndpoint = { + lazy val getExplicitCounterpartiesForAccount : OBPEndpoint = { case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "counterparties" :: Nil JsonGet req => { cc => implicit val ec = EndpointContext(Some(cc)) for { @@ -391,9 +391,9 @@ trait APIMethods220 { resourceDocs += ResourceDoc( - getExplictCounterpartyById, + getExplicitCounterpartyById, implementedInApiVersion, - "getExplictCounterpartyById", + "getExplicitCounterpartyById", "GET", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/counterparties/COUNTERPARTY_ID", "Get Counterparty by Counterparty Id (Explicit)", @@ -407,7 +407,7 @@ trait APIMethods220 { List(apiTagCounterparty, apiTagPSD2PIS, apiTagCounterpartyMetaData, apiTagPsd2) ) - lazy val getExplictCounterpartyById : OBPEndpoint = { + lazy val getExplicitCounterpartyById : OBPEndpoint = { case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "counterparties" :: CounterpartyId(counterpartyId) :: Nil JsonGet req => { cc => implicit val ec = EndpointContext(Some(cc)) for { diff --git a/obp-api/src/main/scala/code/api/v2_2_0/JSONFactory2.2.0.scala b/obp-api/src/main/scala/code/api/v2_2_0/JSONFactory2.2.0.scala index 15eb4e55cb..89ede907ad 100644 --- a/obp-api/src/main/scala/code/api/v2_2_0/JSONFactory2.2.0.scala +++ b/obp-api/src/main/scala/code/api/v2_2_0/JSONFactory2.2.0.scala @@ -185,7 +185,7 @@ case class CounterpartyJsonV220( ) case class CounterpartyMetadataJson( - public_alias : String, // Only have this value when we create explict counterparty + public_alias : String, // Only have this value when we create explicit counterparty more_info : String, url : String, image_url : String, diff --git a/obp-api/src/main/scala/code/api/v2_2_0/OBPAPI2_2_0.scala b/obp-api/src/main/scala/code/api/v2_2_0/OBPAPI2_2_0.scala index 3e3d015c63..d618322c20 100644 --- a/obp-api/src/main/scala/code/api/v2_2_0/OBPAPI2_2_0.scala +++ b/obp-api/src/main/scala/code/api/v2_2_0/OBPAPI2_2_0.scala @@ -178,8 +178,8 @@ object OBPAPI2_2_0 extends OBPRestHelper with APIMethods130 with APIMethods140 w Implementations2_2_0.createViewForBankAccount :: Implementations2_2_0.updateViewForBankAccount :: Implementations2_2_0.getCurrentFxRate :: - Implementations2_2_0.getExplictCounterpartiesForAccount :: - Implementations2_2_0.getExplictCounterpartyById :: + Implementations2_2_0.getExplicitCounterpartiesForAccount :: + Implementations2_2_0.getExplicitCounterpartyById :: Implementations2_2_0.getMessageDocs :: Implementations2_2_0.createBank :: Implementations2_2_0.createAccount :: diff --git a/obp-api/src/main/scala/code/api/v3_0_0/OBPAPI3_0_0.scala b/obp-api/src/main/scala/code/api/v3_0_0/OBPAPI3_0_0.scala index 89678b1961..fad91faae6 100644 --- a/obp-api/src/main/scala/code/api/v3_0_0/OBPAPI3_0_0.scala +++ b/obp-api/src/main/scala/code/api/v3_0_0/OBPAPI3_0_0.scala @@ -209,8 +209,8 @@ object OBPAPI3_0_0 extends OBPRestHelper with APIMethods130 with APIMethods140 w // Possible Endpoints from 2.1.0 val endpointsOf2_2_0 = Implementations2_2_0.getCurrentFxRate :: Implementations2_2_0.createFx :: - Implementations2_2_0.getExplictCounterpartiesForAccount :: - Implementations2_2_0.getExplictCounterpartyById :: + Implementations2_2_0.getExplicitCounterpartiesForAccount :: + Implementations2_2_0.getExplicitCounterpartyById :: Implementations2_2_0.getMessageDocs :: Implementations2_2_0.createBank :: Implementations2_2_0.createAccount :: diff --git a/obp-api/src/main/scala/code/api/v3_1_0/OBPAPI3_1_0.scala b/obp-api/src/main/scala/code/api/v3_1_0/OBPAPI3_1_0.scala index 9881be60bd..b581773be2 100644 --- a/obp-api/src/main/scala/code/api/v3_1_0/OBPAPI3_1_0.scala +++ b/obp-api/src/main/scala/code/api/v3_1_0/OBPAPI3_1_0.scala @@ -209,8 +209,8 @@ object OBPAPI3_1_0 extends OBPRestHelper with APIMethods130 with APIMethods140 w // Possible Endpoints from 2.1.0 val endpointsOf2_2_0 = Implementations2_2_0.getCurrentFxRate :: Implementations2_2_0.createFx :: - Implementations2_2_0.getExplictCounterpartiesForAccount :: - Implementations2_2_0.getExplictCounterpartyById :: + Implementations2_2_0.getExplicitCounterpartiesForAccount :: + Implementations2_2_0.getExplicitCounterpartyById :: Implementations2_2_0.getMessageDocs :: Implementations2_2_0.createBank :: // Implementations2_2_0.createAccount :: 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 aa29368675..56f25e11a9 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 @@ -8363,9 +8363,9 @@ trait APIMethods400 extends MdcLoggable { } staticResourceDocs += ResourceDoc( - getExplictCounterpartiesForAccount, + getExplicitCounterpartiesForAccount, implementedInApiVersion, - "getExplictCounterpartiesForAccount", + "getExplicitCounterpartiesForAccount", "GET", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/counterparties", "Get Counterparties (Explicit)", @@ -8385,13 +8385,13 @@ trait APIMethods400 extends MdcLoggable { ), List(apiTagCounterparty, apiTagPSD2PIS, apiTagPsd2, apiTagAccount)) - lazy val getExplictCounterpartiesForAccount : OBPEndpoint = { + lazy val getExplicitCounterpartiesForAccount : OBPEndpoint = { case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "counterparties" :: Nil JsonGet req => { cc => implicit val ec = EndpointContext(Some(cc)) for { (user @Full(u), _, account, view, callContext) <- SS.userBankAccountView - _ <- Helper.booleanToFuture(failMsg = s"${NoViewPermission}can_add_counterparty", 403, cc=callContext) { - view.canAddCounterparty == true + _ <- Helper.booleanToFuture(failMsg = s"${NoViewPermission}can_get_counterparty", 403, cc=callContext) { + view.canGetCounterparty == true } (counterparties, callContext) <- NewStyle.function.getCounterparties(bankId,accountId,viewId, callContext) //Here we need create the metadata for all the explicit counterparties. maybe show them in json response. @@ -8466,9 +8466,9 @@ trait APIMethods400 extends MdcLoggable { } staticResourceDocs += ResourceDoc( - getExplictCounterpartyById, + getExplicitCounterpartyById, implementedInApiVersion, - "getExplictCounterpartyById", + "getExplicitCounterpartyById", "GET", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/counterparties/COUNTERPARTY_ID", "Get Counterparty by Id (Explicit)", @@ -8482,7 +8482,7 @@ trait APIMethods400 extends MdcLoggable { List(apiTagCounterparty, apiTagPSD2PIS, apiTagPsd2, apiTagCounterpartyMetaData) ) - lazy val getExplictCounterpartyById : OBPEndpoint = { + lazy val getExplicitCounterpartyById : OBPEndpoint = { case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "counterparties" :: CounterpartyId(counterpartyId) :: Nil JsonGet req => { cc => implicit val ec = EndpointContext(Some(cc)) for { 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 ac57b988a5..5b34388171 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 @@ -15,7 +15,7 @@ import code.api.v2_1_0.JSONFactory210 import code.api.v3_0_0.JSONFactory300 import code.api.v3_1_0._ import code.api.v4_0_0.JSONFactory400.createCustomersMinimalJson -import code.api.v4_0_0.{JSONFactory400, OBPAPI4_0_0, PutProductJsonV400} +import code.api.v4_0_0.{JSONFactory400, OBPAPI4_0_0, PostCounterpartyJson400, PutProductJsonV400} import code.api.v5_0_0.JSONFactory500.{createPhysicalCardJson, createViewJsonV500, createViewsIdsJsonV500, createViewsJsonV500} import code.bankconnectors.Connector import code.consent.{ConsentRequest, ConsentRequests, Consents} @@ -44,11 +44,13 @@ import java.util.concurrent.ThreadLocalRandom import code.accountattribute.AccountAttributeX import code.api.Constant.SYSTEM_OWNER_VIEW_ID import code.api.util.FutureUtil.EndpointContext -import code.api.v5_1_0.PostConsentRequestJsonV510 +import code.api.v5_1_0.{CreateCustomViewJson, PostVRPConsentRequestJsonV510, PostCounterpartyLimitV510} import code.consumer.Consumers +import code.metadata.counterparties.MappedCounterparty import code.util.Helper.booleanToFuture import code.views.system.{AccountAccess, ViewDefinition} +import java.util.UUID import scala.collection.immutable.{List, Nil} import scala.collection.mutable.ArrayBuffer import scala.concurrent.Future @@ -927,18 +929,154 @@ trait APIMethods500 { _ <- Helper.booleanToFuture(ConsentAllowedScaMethods, cc=callContext){ List(StrongCustomerAuthentication.SMS.toString(), StrongCustomerAuthentication.EMAIL.toString(), StrongCustomerAuthentication.IMPLICIT.toString()).exists(_ == scaMethod) } - consentRequestJson <- + (consentRequestJson, isVRPConsentRequest) <- if(createdConsentRequest.payload.contains("to_account")) { - val failMsg = s"$InvalidJsonFormat The Json body should be the $PostConsentBodyCommonJson " + val failMsg = s"$InvalidJsonFormat The vrp consent request json body should be the $PostVRPConsentRequestJsonV510 " NewStyle.function.tryons(failMsg, 400, callContext) { - json.parse(createdConsentRequest.payload).extract[code.api.v5_1_0.PostConsentRequestJsonV510] - }.map(_.toPostConsentRequestJsonV500) + json.parse(createdConsentRequest.payload).extract[code.api.v5_1_0.PostVRPConsentRequestJsonInternalV510] + }.map(postVRPConsentRequest => (postVRPConsentRequest.toPostConsentRequestJsonV500, true)) } else{ - val failMsg = s"$InvalidJsonFormat The Json body should be the $PostConsentBodyCommonJson " + val failMsg = s"$InvalidJsonFormat The consent request Json body should be the $PostConsentRequestJsonV500 " NewStyle.function.tryons(failMsg, 400, callContext) { json.parse(createdConsentRequest.payload).extract[PostConsentRequestJsonV500] + }.map(postVRPConsentRequest => (postVRPConsentRequest, false)) + } + + //Here are all the VRP consent request + (bankId, accountId, viewId, counterpartyId) <- if (isVRPConsentRequest) { + val postConsentRequestJsonV510 = json.parse(createdConsentRequest.payload).extract[code.api.v5_1_0.PostVRPConsentRequestJsonV510] + val fromBankIdAccountId = BankIdAccountId(BankId(postConsentRequestJsonV510.from_account.bank_routing.address), AccountId(postConsentRequestJsonV510.from_account.account_routing.address)) + + val vrpViewId = s"_VRP-${UUID.randomUUID.toString}".dropRight(5)// to make sure the length of the viewId is 36. + val targetPermissions = List(//may need getTransactionRequest .. so far only this payments. + "can_add_transaction_request_to_beneficiary" + ) + + val targetCreateCustomViewJson = CreateCustomViewJson( + name = vrpViewId, + description = vrpViewId, + metadata_view = vrpViewId, + is_public = false, + which_alias_to_use = vrpViewId, + hide_metadata_if_alias_used = true, + allowed_permissions = targetPermissions + ) + + for { + //1st: create the Custom View for the from account. + (fromAccount, callContext) <- NewStyle.function.checkBankAccountExists(fromBankIdAccountId.bankId, fromBankIdAccountId.accountId, callContext) + //we do not need sourceViewId so far, we need to get all the view access for the login user, and + + permission <- NewStyle.function.permission(fromAccount.bankId, fromAccount.accountId, user, callContext) + + permissionsFromSource = permission.views.map(view =>APIUtil.getViewPermissions(view.asInstanceOf[ViewDefinition]).toList).flatten.toSet + permissionsFromTarget = targetCreateCustomViewJson.allowed_permissions + + userMissingPermissions = permissionsFromTarget.toSet diff permissionsFromSource + + failMsg = s"${ErrorMessages.UserDoesNotHavePermission} ${userMissingPermissions.toString}" + _ <- Helper.booleanToFuture(failMsg, cc = callContext) { + userMissingPermissions.isEmpty + } + (vrpView, callContext) <- NewStyle.function.createCustomView(fromBankIdAccountId, targetCreateCustomViewJson.toCreateViewJson, callContext) + + _ <-NewStyle.function.grantAccessToCustomView(vrpView, user, callContext) + + //2st: Create a new counterparty on that view (_VRP-9d429899-24f5-42c8-8565-943ffa6a7945) + postJson = PostCounterpartyJson400( + name = postConsentRequestJsonV510.to_account.counterparty_name, + description = postConsentRequestJsonV510.to_account.counterparty_name, + currency = postConsentRequestJsonV510.to_account.limit.currency, + other_account_routing_scheme = postConsentRequestJsonV510.to_account.account_routing.scheme, + other_account_routing_address = postConsentRequestJsonV510.to_account.account_routing.scheme, + other_account_secondary_routing_scheme = "", + other_account_secondary_routing_address = "", + other_bank_routing_scheme = postConsentRequestJsonV510.to_account.account_routing.scheme, + other_bank_routing_address = postConsentRequestJsonV510.to_account.account_routing.scheme, + other_branch_routing_scheme = postConsentRequestJsonV510.to_account.account_routing.scheme, + other_branch_routing_address = postConsentRequestJsonV510.to_account.account_routing.scheme, + is_beneficiary = true, + bespoke = Nil + ) + _ <- Helper.booleanToFuture(s"$InvalidValueLength. The maximum length of `description` field is ${MappedCounterparty.mDescription.maxLen}", cc = callContext) { + postJson.description.length <= 36 + } + + + (counterparty, callContext) <- Connector.connector.vend.checkCounterpartyExists(postJson.name, fromBankIdAccountId.bankId.value, fromBankIdAccountId.accountId.value, vrpView.viewId.value, callContext) + + _ <- Helper.booleanToFuture(CounterpartyAlreadyExists.replace("value for BANK_ID or ACCOUNT_ID or VIEW_ID or NAME.", + s"COUNTERPARTY_NAME(${postJson.name}) for the BANK_ID(${fromBankIdAccountId.bankId.value}) and ACCOUNT_ID(${fromBankIdAccountId.accountId.value}) and VIEW_ID($vrpViewId)"), cc = callContext) { + counterparty.isEmpty + } + + _ <- Helper.booleanToFuture(s"$InvalidISOCurrencyCode Current input is: '${postJson.currency}'", cc = callContext) { + isValidCurrencyISOCode(postJson.currency) + } + + (counterparty, callContext) <- NewStyle.function.createCounterparty( + name = postJson.name, + description = postJson.description, + currency = postJson.currency, + createdByUserId = user.userId, + thisBankId = fromBankIdAccountId.bankId.value, + thisAccountId = fromBankIdAccountId.accountId.value, + thisViewId = vrpViewId, + otherAccountRoutingScheme = postJson.other_account_routing_scheme, + otherAccountRoutingAddress = postJson.other_account_routing_address, + otherAccountSecondaryRoutingScheme = postJson.other_account_secondary_routing_scheme, + otherAccountSecondaryRoutingAddress = postJson.other_account_secondary_routing_address, + otherBankRoutingScheme = postJson.other_bank_routing_scheme, + otherBankRoutingAddress = postJson.other_bank_routing_address, + otherBranchRoutingScheme = postJson.other_branch_routing_scheme, + otherBranchRoutingAddress = postJson.other_branch_routing_address, + isBeneficiary = postJson.is_beneficiary, + bespoke = postJson.bespoke.map(bespoke => CounterpartyBespoke(bespoke.key, bespoke.value)), + callContext + ) + + + postCounterpartyLimitV510 = PostCounterpartyLimitV510( + currency = postConsentRequestJsonV510.to_account.limit.currency, + max_single_amount = postConsentRequestJsonV510.to_account.limit.max_single_amount, + max_monthly_amount = postConsentRequestJsonV510.to_account.limit.max_monthly_amount, + max_number_of_monthly_transactions = postConsentRequestJsonV510.to_account.limit.max_number_of_monthly_transactions, + max_yearly_amount = postConsentRequestJsonV510.to_account.limit.max_yearly_amount, + max_number_of_yearly_transactions = postConsentRequestJsonV510.to_account.limit.max_number_of_yearly_transactions + ) + //3rd: create the counterparty limit + (counterpartyLimitBox, callContext) <- Connector.connector.vend.getCounterpartyLimit( + fromBankIdAccountId.bankId.value, + fromBankIdAccountId.accountId.value, + vrpViewId, + counterparty.counterpartyId, + cc.callContext + ) + failMsg = s"$CounterpartyLimitAlreadyExists Current BANK_ID(${fromBankIdAccountId.bankId.value}), ACCOUNT_ID(${fromBankIdAccountId.accountId.value}), VIEW_ID($vrpViewId),COUNTERPARTY_ID(${counterparty.counterpartyId})" + _ <- Helper.booleanToFuture(failMsg, cc = callContext) { + counterpartyLimitBox.isEmpty } + (counterpartyLimit, callContext) <- NewStyle.function.createOrUpdateCounterpartyLimit( + bankId = counterparty.thisBankId, + accountId = counterparty.thisAccountId, + viewId = counterparty.thisViewId, + counterpartyId = counterparty.counterpartyId, + postCounterpartyLimitV510.currency, + postCounterpartyLimitV510.max_single_amount, + postCounterpartyLimitV510.max_monthly_amount, + postCounterpartyLimitV510.max_number_of_monthly_transactions, + postCounterpartyLimitV510.max_yearly_amount, + postCounterpartyLimitV510.max_number_of_yearly_transactions, + cc.callContext + ) + + } yield { + (fromAccount.bankId, fromAccount.accountId, vrpView.viewId, CounterpartyId(counterparty.counterpartyId)) } + }else{ + Future.successful(BankId(""), AccountId(""), ViewId(""),CounterpartyId("")) + } + maxTimeToLive = APIUtil.getPropsAsIntValue(nameOfProperty="consents.max_time_to_live", defaultValue=3600) _ <- Helper.booleanToFuture(s"$ConsentMaxTTL ($maxTimeToLive)", cc=callContext){ consentRequestJson.time_to_live match { @@ -963,17 +1101,25 @@ trait APIMethods500 { ) ) } - postConsentViewJsons <- Future.sequence( - consentRequestJson.account_access.map( - access => - NewStyle.function.getBankAccountByRouting(None,access.account_routing.scheme, access.account_routing.address, cc.callContext) - .map(result =>PostConsentViewJsonV310( - result._1.bankId.value, - result._1.accountId.value, - access.view_id - )) + postConsentViewJsons <- if(createdConsentRequest.payload.contains("to_account")) { + Future.successful(List(PostConsentViewJsonV310( + bankId.value, + accountId.value, + viewId.value + ))) + }else{ + Future.sequence( + consentRequestJson.account_access.map( + access => + NewStyle.function.getBankAccountByRouting(None,access.account_routing.scheme, access.account_routing.address, cc.callContext) + .map(result =>PostConsentViewJsonV310( + result._1.bankId.value, + result._1.accountId.value, + access.view_id + )) ) ) + } (_, assignedViews) <- Future(Views.views.vend.privateViewsUserCanAccess(user)) _ <- Helper.booleanToFuture(ViewsAllowedInConsent, cc=callContext){ @@ -1058,7 +1204,13 @@ trait APIMethods500 { case _ =>Future{"Success"} } } yield { - (ConsentJsonV500(createdConsent.consentId, consentJWT, createdConsent.status, Some(createdConsent.consentRequestId)), HttpCode.`201`(callContext)) + (ConsentJsonV500( + createdConsent.consentId, + consentJWT, + createdConsent.status, + Some(createdConsent.consentRequestId), + if (isVRPConsentRequest) Some(ConsentAccountAccessJson(bankId.value, accountId.value, viewId.value, HelperInfoJson(List(counterpartyId.value)))) else None + ), HttpCode.`201`(callContext)) } } } diff --git a/obp-api/src/main/scala/code/api/v5_0_0/JSONFactory5.0.0.scala b/obp-api/src/main/scala/code/api/v5_0_0/JSONFactory5.0.0.scala index b424dc291b..8ca40ad23b 100644 --- a/obp-api/src/main/scala/code/api/v5_0_0/JSONFactory5.0.0.scala +++ b/obp-api/src/main/scala/code/api/v5_0_0/JSONFactory5.0.0.scala @@ -233,8 +233,19 @@ case class PostConsentRequestJsonV500( valid_from: Option[Date], time_to_live: Option[Long] ) +case class HelperInfoJson( + counterparty_id:List[String] +) + +case class ConsentAccountAccessJson( + bank_id:String, + account_id:String, + view_id:String, + helper_info: HelperInfoJson +) + -case class ConsentJsonV500(consent_id: String, jwt: String, status: String, consent_request_id: Option[String]) +case class ConsentJsonV500(consent_id: String, jwt: String, status: String, consent_request_id: Option[String], account_access:Option[ConsentAccountAccessJson] = None) case class CreatePhysicalCardJsonV500( card_number: String, diff --git a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala index 5ce86b3f39..ab1aa17571 100644 --- a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala +++ b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala @@ -49,7 +49,8 @@ import com.openbankproject.commons.model._ import com.openbankproject.commons.util.{ApiVersion, ScannedApiVersion} import net.liftweb.common.Full import net.liftweb.http.rest.RestHelper -import net.liftweb.json.{Extraction, compactRender, parse, prettyRender} +import net.liftweb.json +import net.liftweb.json.{Extraction, JObject, compactRender, parse, prettyRender} import net.liftweb.mapper.By import net.liftweb.util.{Helpers, StringHelpers} import net.liftweb.util.Helpers.tryo @@ -57,7 +58,7 @@ import net.liftweb.util.Helpers.tryo import scala.collection.immutable.{List, Nil} import scala.collection.mutable.ArrayBuffer import scala.concurrent.Future - +import com.openbankproject.commons.model.enums.ConsentType trait APIMethods510 { self: RestHelper => @@ -2782,7 +2783,7 @@ trait APIMethods510 { |Authorization: Bearer eXtneO-THbQtn3zvK_kQtXXfvOZyZFdBCItlPDbR2Bk.dOWqtXCtFX-tqGTVR0YrIjvAolPIVg7GZ-jz83y6nA0 | |""".stripMargin, - postConsentRequestJsonV510, + postVRPConsentRequestJsonV510, vrpConsentRequestResponseJson, List( InvalidJsonFormat, @@ -2796,25 +2797,29 @@ trait APIMethods510 { ) lazy val createVRPConsentRequest : OBPEndpoint = { - case "consumer" :: "vrp-consent-requests" :: Nil JsonPost json -> _ => { + case "consumer" :: "vrp-consent-requests" :: Nil JsonPost postJson -> _ => { cc => implicit val ec = EndpointContext(Some(cc)) for { (_, callContext) <- applicationAccess(cc) _ <- passesPsd2Aisp(callContext) - failMsg = s"$InvalidJsonFormat The Json body should be the $PostConsentRequestJsonV510 " - consentRequestJson: PostConsentRequestJsonV510 <- NewStyle.function.tryons(failMsg, 400, callContext) { - json.extract[PostConsentRequestJsonV510] + failMsg = s"$InvalidJsonFormat The Json body should be the $PostVRPConsentRequestJsonV510 " + consentRequestJson: PostVRPConsentRequestJsonV510 <- NewStyle.function.tryons(failMsg, 400, callContext) { + postJson.extract[PostVRPConsentRequestJsonV510] } - maxTimeToLive = APIUtil.getPropsAsIntValue(nameOfProperty="consents.max_time_to_live", defaultValue=3600) - _ <- Helper.booleanToFuture(s"$ConsentMaxTTL ($maxTimeToLive)", cc=callContext){ + maxTimeToLive = APIUtil.getPropsAsIntValue(nameOfProperty = "consents.max_time_to_live", defaultValue = 3600) + _ <- Helper.booleanToFuture(s"$ConsentMaxTTL ($maxTimeToLive)", cc = callContext) { consentRequestJson.time_to_live match { case Some(ttl) => ttl <= maxTimeToLive case _ => true } } + + // we need to add the consent_type internally, the user does not need to know it. + consentType = json.parse(s"""{"consent_type": "${ConsentType.VRP}"}""") + createdConsentRequest <- Future(ConsentRequests.consentRequestProvider.vend.createConsentRequest( callContext.flatMap(_.consumer), - Some(compactRender(json)) + Some(compactRender(postJson merge consentType)) )) map { i => connectorEmptyResponse(i, callContext) } diff --git a/obp-api/src/main/scala/code/api/v5_1_0/JSONFactory5.1.0.scala b/obp-api/src/main/scala/code/api/v5_1_0/JSONFactory5.1.0.scala index e9a12640b2..428be9ab19 100644 --- a/obp-api/src/main/scala/code/api/v5_1_0/JSONFactory5.1.0.scala +++ b/obp-api/src/main/scala/code/api/v5_1_0/JSONFactory5.1.0.scala @@ -35,7 +35,7 @@ import code.api.v2_1_0.ResourceUserJSON import code.api.v3_0_0.JSONFactory300.{createLocationJson, createMetaJson, transformToAddressFromV300} import code.api.v3_0_0.{AccountIdJson, AccountsIdsJsonV300, AddressJsonV300, OpeningTimesV300, ViewJsonV300} import code.api.v4_0_0.{EnergySource400, HostedAt400, HostedBy400, PostViewJsonV400} -import code.api.v5_0_0.{PostConsentRequestJsonV500} +import code.api.v5_0_0.PostConsentRequestJsonV500 import code.atmattribute.AtmAttribute import code.atms.Atms.Atm import code.users.{UserAttribute, Users} @@ -47,6 +47,7 @@ import java.util.Date import code.consent.MappedConsent import code.metrics.APIMetric import code.model.Consumer +import com.openbankproject.commons.model.enums.ConsentType import net.liftweb.common.{Box, Full} import net.liftweb.json import net.liftweb.json.{JString, JValue, parse, parseOpt} @@ -393,20 +394,21 @@ case class ConsentRequestFromAccountJson( ) case class ConsentRequestToAccountJson( + counterparty_name: String, bank_routing: BankRoutingJsonV121, account_routing: AccountRoutingJsonV121, branch_routing: BranchRoutingJsonV141, limit: PostCounterpartyLimitV510 ) -case class PostConsentRequestJsonV510( - from_account:ConsentRequestFromAccountJson, - to_account:ConsentRequestToAccountJson, +case class PostVRPConsentRequestJsonInternalV510( + consent_type: String, + from_account: ConsentRequestFromAccountJson, + to_account: ConsentRequestToAccountJson, email: Option[String], phone_number: Option[String], valid_from: Option[Date], - time_to_live: Option[Long] -){ + time_to_live: Option[Long]) { def toPostConsentRequestJsonV500 = { PostConsentRequestJsonV500( everything = false, @@ -422,6 +424,15 @@ case class PostConsentRequestJsonV510( } } +case class PostVRPConsentRequestJsonV510( + from_account:ConsentRequestFromAccountJson, + to_account:ConsentRequestToAccountJson, + email: Option[String], + phone_number: Option[String], + valid_from: Option[Date], + time_to_live: Option[Long] +) + object JSONFactory510 extends CustomJsonFormats { def createViewJson(view: View): CustomViewJsonV510 = { diff --git a/obp-api/src/main/scala/code/bankconnectors/Connector.scala b/obp-api/src/main/scala/code/bankconnectors/Connector.scala index 1e68c3ed21..a4c3ebdc5d 100644 --- a/obp-api/src/main/scala/code/bankconnectors/Connector.scala +++ b/obp-api/src/main/scala/code/bankconnectors/Connector.scala @@ -521,7 +521,7 @@ trait Connector extends MdcLoggable { * If it is Mapped connector: * OBP will return all the accounts from accountHolder * @param username username of the user. - * @param callContext inside, should contains the proper values for CBS to identify a bank Customer + * @param callContext inside, should contain the proper values for CBS to identify a bank Customer * @return all the accounts, get from Main Frame. */ def getBankAccountsForUser(provider: String, username:String, callContext: Option[CallContext]) : Future[Box[(List[InboundAccount], Option[CallContext])]] = Future{ diff --git a/obp-api/src/main/scala/code/bankconnectors/ConnectorBuilderUtil.scala b/obp-api/src/main/scala/code/bankconnectors/ConnectorBuilderUtil.scala index 4bf6e97bdd..937fa0e9c3 100644 --- a/obp-api/src/main/scala/code/bankconnectors/ConnectorBuilderUtil.scala +++ b/obp-api/src/main/scala/code/bankconnectors/ConnectorBuilderUtil.scala @@ -359,6 +359,7 @@ object ConnectorBuilderUtil { "createDirectDebit", "deleteCustomerAttribute", "getPhysicalCardsForUser", + "getChallengesByBasketId", // The follow methods's parameter or return type are special "getCurrentFxRate", diff --git a/obp-api/src/main/scala/code/bankconnectors/storedprocedure/StoredProcedureConnector_vDec2019.scala b/obp-api/src/main/scala/code/bankconnectors/storedprocedure/StoredProcedureConnector_vDec2019.scala index 72e5c1b98c..e0954ea6e9 100644 --- a/obp-api/src/main/scala/code/bankconnectors/storedprocedure/StoredProcedureConnector_vDec2019.scala +++ b/obp-api/src/main/scala/code/bankconnectors/storedprocedure/StoredProcedureConnector_vDec2019.scala @@ -75,7 +75,7 @@ trait StoredProcedureConnector_vDec2019 extends Connector with MdcLoggable { val connectorName = "stored_procedure_vDec2019" //---------------- dynamic start -------------------please don't modify this line -// ---------- created on 2024-07-06T09:03:12Z +// ---------- created on 2024-08-07T12:43:02Z messageDocs += getAdapterInfoDoc def getAdapterInfoDoc = MessageDoc( @@ -444,7 +444,7 @@ trait StoredProcedureConnector_vDec2019 extends Connector with MdcLoggable { exampleOutboundMessage = ( OutBoundValidateChallengeAnswerV2(outboundAdapterCallContext=MessageDocsSwaggerDefinitions.outboundAdapterCallContext, challengeId=challengeIdExample.value, - suppliedAnswer="string", + suppliedAnswer=suppliedAnswerExample.value, suppliedAnswerType=com.openbankproject.commons.model.enums.SuppliedAnswerType.example) ), exampleInboundMessage = ( @@ -557,7 +557,7 @@ trait StoredProcedureConnector_vDec2019 extends Connector with MdcLoggable { transactionRequestId=Some(transactionRequestIdExample.value), consentId=Some(consentIdExample.value), challengeId=challengeIdExample.value, - suppliedAnswer="string", + suppliedAnswer=suppliedAnswerExample.value, suppliedAnswerType=com.openbankproject.commons.model.enums.SuppliedAnswerType.example) ), exampleInboundMessage = ( @@ -706,6 +706,44 @@ trait StoredProcedureConnector_vDec2019 extends Connector with MdcLoggable { response.map(convertToTuple[List[ChallengeCommons]](callContext)) } + messageDocs += getChallengesByBasketIdDoc + def getChallengesByBasketIdDoc = MessageDoc( + process = "obp.getChallengesByBasketId", + messageFormat = messageFormat, + description = "Get Challenges By Basket Id", + outboundTopic = None, + inboundTopic = None, + exampleOutboundMessage = ( + OutBoundGetChallengesByBasketId(outboundAdapterCallContext=MessageDocsSwaggerDefinitions.outboundAdapterCallContext, + basketId=basketIdExample.value) + ), + exampleInboundMessage = ( + InBoundGetChallengesByBasketId(inboundAdapterCallContext=MessageDocsSwaggerDefinitions.inboundAdapterCallContext, + status=MessageDocsSwaggerDefinitions.inboundStatus, + data=List( ChallengeCommons(challengeId=challengeIdExample.value, + transactionRequestId=transactionRequestIdExample.value, + expectedAnswer="string", + expectedUserId="string", + salt="string", + successful=true, + challengeType=challengeTypeExample.value, + consentId=Some(consentIdExample.value), + basketId=Some(basketIdExample.value), + scaMethod=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthentication.SMS), + scaStatus=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthenticationStatus.example), + authenticationMethodId=Some("string"), + attemptCounter=123))) + ), + adapterImplementation = Some(AdapterImplementation("- Core", 1)) + ) + + override def getChallengesByBasketId(basketId: String, callContext: Option[CallContext]): OBPReturnType[Box[List[ChallengeTrait]]] = { + import com.openbankproject.commons.dto.{InBoundGetChallengesByBasketId => InBound, OutBoundGetChallengesByBasketId => OutBound} + val req = OutBound(callContext.map(_.toOutboundAdapterCallContext).orNull, basketId) + val response: Future[Box[InBound]] = sendRequest[InBound]("obp_get_challenges_by_basket_id", req, callContext) + response.map(convertToTuple[List[ChallengeCommons]](callContext)) + } + messageDocs += getChallengeDoc def getChallengeDoc = MessageDoc( process = "obp.getChallenge", @@ -6737,8 +6775,8 @@ trait StoredProcedureConnector_vDec2019 extends Connector with MdcLoggable { response.map(convertToTuple[Boolean](callContext)) } -// ---------- created on 2024-07-06T09:03:12Z -//---------------- dynamic end ---------------------please don't modify this line +// ---------- created on 2024-08-07T12:43:02Z +//---------------- dynamic end ---------------------please don't modify this line private val availableOperation = DynamicEntityOperation.values.map(it => s""""$it"""").mkString("[", ", ", "]") diff --git a/obp-api/src/main/scala/code/consent/MappedConsent.scala b/obp-api/src/main/scala/code/consent/MappedConsent.scala index 37734773da..56f463ff1c 100644 --- a/obp-api/src/main/scala/code/consent/MappedConsent.scala +++ b/obp-api/src/main/scala/code/consent/MappedConsent.scala @@ -7,6 +7,7 @@ import code.consent.ConsentStatus.ConsentStatus import code.model.Consumer import code.util.MappedUUID import com.openbankproject.commons.model.User +import com.openbankproject.commons.util.ApiStandards import net.liftweb.common.{Box, Empty, Failure, Full} import net.liftweb.mapper.{MappedString, _} import net.liftweb.util.Helpers.{now, tryo} @@ -74,6 +75,12 @@ object MappedConsentProvider extends ConsentProvider { .mChallenge(challengeAnswerHashed) .mSalt(salt) .mStatus(ConsentStatus.INITIATED.toString) + .mRecurringIndicator(true) + .mFrequencyPerDay(100) + .mUsesSoFarTodayCounter(0) + .mUsesSoFarTodayCounterUpdatedAt(new Date()) + .mLastActionDate(now) //maybe not right, but for the create we use the `now`, we need to update it later. + .mApiStandard(ApiStandards.obp.toString) .saveMe() } } diff --git a/obp-api/src/main/scala/code/metadata/counterparties/MapperCounterparties.scala b/obp-api/src/main/scala/code/metadata/counterparties/MapperCounterparties.scala index f6eff475e8..b3bb9b5f82 100644 --- a/obp-api/src/main/scala/code/metadata/counterparties/MapperCounterparties.scala +++ b/obp-api/src/main/scala/code/metadata/counterparties/MapperCounterparties.scala @@ -211,7 +211,7 @@ object MapperCounterparties extends Counterparties with MdcLoggable { ): Box[CounterpartyTrait] = { val mappedCounterparty = MappedCounterparty.create - .mCounterPartyId(APIUtil.createExplicitCounterpartyId) //We create the Counterparty_Id here, it means, it will be create in each connnector. + .mCounterPartyId(APIUtil.createExplicitCounterpartyId) //We create the Counterparty_Id here, it means, it will be create in each connector. .mName(name) .mCreatedByUserId(createdByUserId) .mThisBankId(thisBankId) diff --git a/obp-api/src/main/scala/code/model/BankingData.scala b/obp-api/src/main/scala/code/model/BankingData.scala index 086d1692cb..b8381fae4a 100644 --- a/obp-api/src/main/scala/code/model/BankingData.scala +++ b/obp-api/src/main/scala/code/model/BankingData.scala @@ -388,11 +388,11 @@ case class BankAccountExtended(val bankAccount: BankAccount) extends MdcLoggable final def moderatedOtherBankAccounts(view : View, bankIdAccountId: BankIdAccountId, user : Box[User], callContext: Option[CallContext]) : Box[List[ModeratedOtherBankAccount]] = if(APIUtil.hasAccountAccess(view, bankIdAccountId, user, callContext)){ val implicitModeratedOtherBankAccounts = Connector.connector.vend.getCounterpartiesFromTransaction(bankId, accountId).openOrThrowException(attemptedToOpenAnEmptyBox).map(oAcc => view.moderateOtherAccount(oAcc)).flatten - val explictCounterpartiesBox = Connector.connector.vend.getCounterpartiesLegacy(view.bankId, view.accountId, view.viewId) - explictCounterpartiesBox match { + val explicitCounterpartiesBox = Connector.connector.vend.getCounterpartiesLegacy(view.bankId, view.accountId, view.viewId) + explicitCounterpartiesBox match { case Full((counterparties, callContext))=> { - val explictModeratedOtherBankAccounts: List[ModeratedOtherBankAccount] = counterparties.flatMap(BankAccountX.toInternalCounterparty).flatMap(counterparty=>view.moderateOtherAccount(counterparty)) - Full(explictModeratedOtherBankAccounts ++ implicitModeratedOtherBankAccounts) + val explicitModeratedOtherBankAccounts: List[ModeratedOtherBankAccount] = counterparties.flatMap(BankAccountX.toInternalCounterparty).flatMap(counterparty=>view.moderateOtherAccount(counterparty)) + Full(explicitModeratedOtherBankAccounts ++ implicitModeratedOtherBankAccounts) } case _ => Full(implicitModeratedOtherBankAccounts) } @@ -409,7 +409,7 @@ case class BankAccountExtended(val bankAccount: BankAccount) extends MdcLoggable final def moderatedOtherBankAccount(counterpartyID : String, view : View, bankIdAccountId: BankIdAccountId, user : Box[User], callContext: Option[CallContext]) : Box[ModeratedOtherBankAccount] = if(APIUtil.hasAccountAccess(view, bankIdAccountId, user, callContext)) Connector.connector.vend.getCounterpartyByCounterpartyIdLegacy(CounterpartyId(counterpartyID), None).map(_._1).flatMap(BankAccountX.toInternalCounterparty).flatMap(view.moderateOtherAccount) match { - //First check the explict counterparty + //First check the explicit counterparty case Full(moderatedOtherBankAccount) => Full(moderatedOtherBankAccount) //Than we checked the implict counterparty. case _ => Connector.connector.vend.getCounterpartyFromTransaction(bankId, accountId, counterpartyID).flatMap(oAcc => view.moderateOtherAccount(oAcc)) diff --git a/obp-api/src/main/scala/code/model/User.scala b/obp-api/src/main/scala/code/model/User.scala index 891cc1f839..bc8d2906a4 100644 --- a/obp-api/src/main/scala/code/model/User.scala +++ b/obp-api/src/main/scala/code/model/User.scala @@ -66,7 +66,7 @@ case class UserExtended(val user: User) extends MdcLoggable { val consumerAccountAccess = { //If we find the AccountAccess by consumerId, this mean the accountAccess already assigned to some consumers - val explictConsumerHasAccountAccess = if(consumerId.isDefined){ + val explicitConsumerHasAccountAccess = if(consumerId.isDefined){ AccountAccess.find( By(AccountAccess.bank_id, bankIdAccountId.bankId.value), By(AccountAccess.account_id, bankIdAccountId.accountId.value), @@ -77,7 +77,7 @@ case class UserExtended(val user: User) extends MdcLoggable { false } - if(explictConsumerHasAccountAccess) { + if(explicitConsumerHasAccountAccess) { true }else{ //If we can not find accountAccess by consumerId, then we will find AccountAccess by default "ALL_CONSUMERS" , this mean the accountAccess can be used for all consumers diff --git a/obp-api/src/main/scala/code/model/dataAccess/MappedView.scala b/obp-api/src/main/scala/code/model/dataAccess/MappedView.scala index c5c3c156b8..abaf09f344 100644 --- a/obp-api/src/main/scala/code/model/dataAccess/MappedView.scala +++ b/obp-api/src/main/scala/code/model/dataAccess/MappedView.scala @@ -442,6 +442,9 @@ class ViewImpl extends View with LongKeyedMapper[ViewImpl] with ManyToMany with object canAddTransactionRequestToAnyAccount_ extends MappedBoolean(this){ override def defaultValue = false } + object canAddTransactionRequestToBeneficiary_ extends MappedBoolean(this){ + override def defaultValue = false + } object canSeeBankAccountCreditLimit_ extends MappedBoolean(this){ override def defaultValue = false } @@ -589,6 +592,8 @@ class ViewImpl extends View with LongKeyedMapper[ViewImpl] with ManyToMany with override def canGrantAccessToCustomViews: Boolean = canGrantAccessToCustomViews_.get override def canRevokeAccessToCustomViews: Boolean = canRevokeAccessToCustomViews_.get + + override def canAddTransactionRequestToBeneficiary: Boolean = canAddTransactionRequestToBeneficiary_.get } object ViewImpl extends ViewImpl with LongKeyedMetaMapper[ViewImpl]{ diff --git a/obp-api/src/main/scala/code/signingbaskets/MappedSigningBasketProvider.scala b/obp-api/src/main/scala/code/signingbaskets/MappedSigningBasketProvider.scala index eb2ab958c5..b39568d052 100644 --- a/obp-api/src/main/scala/code/signingbaskets/MappedSigningBasketProvider.scala +++ b/obp-api/src/main/scala/code/signingbaskets/MappedSigningBasketProvider.scala @@ -14,11 +14,11 @@ object MappedSigningBasketProvider extends SigningBasketProvider { override def getSigningBasketByBasketId(entityId: String): Box[SigningBasketContent] = { val basket: Box[MappedSigningBasket] = MappedSigningBasket.find(By(MappedSigningBasket.BasketId, entityId)) - val payments = MappedSigningBasketPayment.findAll(By(MappedSigningBasketPayment.BasketId, entityId)).map(_.basketId) match { + val payments = MappedSigningBasketPayment.findAll(By(MappedSigningBasketPayment.BasketId, entityId)).map(_.paymentId) match { case Nil => None case head :: tail => Some(head :: tail) } - val consents = MappedSigningBasketConsent.findAll(By(MappedSigningBasketConsent.BasketId, entityId)).map(_.basketId) match { + val consents = MappedSigningBasketConsent.findAll(By(MappedSigningBasketConsent.BasketId, entityId)).map(_.consentId) match { case Nil => None case head :: tail => Some(head :: tail) } @@ -26,11 +26,11 @@ object MappedSigningBasketProvider extends SigningBasketProvider { } override def saveSigningBasketStatus(entityId: String, status: String): Box[SigningBasketContent] = { val basket: Box[MappedSigningBasket] = MappedSigningBasket.find(By(MappedSigningBasket.BasketId, entityId)).map(_.Status(status).saveMe) - val payments = MappedSigningBasketPayment.findAll(By(MappedSigningBasketPayment.BasketId, entityId)).map(_.basketId) match { + val payments = MappedSigningBasketPayment.findAll(By(MappedSigningBasketPayment.BasketId, entityId)).map(_.paymentId) match { case Nil => None case head :: tail => Some(head :: tail) } - val consents = MappedSigningBasketConsent.findAll(By(MappedSigningBasketConsent.BasketId, entityId)).map(_.basketId) match { + val consents = MappedSigningBasketConsent.findAll(By(MappedSigningBasketConsent.BasketId, entityId)).map(_.consentId) match { case Nil => None case head :: tail => Some(head :: tail) } diff --git a/obp-api/src/main/scala/code/views/MapperViews.scala b/obp-api/src/main/scala/code/views/MapperViews.scala index 734c35012b..c28a5063e0 100644 --- a/obp-api/src/main/scala/code/views/MapperViews.scala +++ b/obp-api/src/main/scala/code/views/MapperViews.scala @@ -907,6 +907,7 @@ object MapperViews extends Views with MdcLoggable { // TODO Allow use only for certain cases .canAddTransactionRequestToOwnAccount_(true) //added following two for payments .canAddTransactionRequestToAnyAccount_(true) + .canAddTransactionRequestToBeneficiary_(true) .canSeeAvailableViewsForBankAccount_(false) .canSeeTransactionRequests_(false) @@ -936,6 +937,7 @@ object MapperViews extends Views with MdcLoggable { entity // Make additional setup to the existing view .canSeeTransactionDescription_(false) .canAddTransactionRequestToAnyAccount_(false) + .canAddTransactionRequestToBeneficiary_(false) case SYSTEM_MANAGE_CUSTOM_VIEWS_VIEW_ID => entity // Make additional setup to the existing view .canRevokeAccessToCustomViews_(true) @@ -978,6 +980,7 @@ object MapperViews extends Views with MdcLoggable { .view_id(viewId) .description_(viewId) .canAddTransactionRequestToAnyAccount_(true) + .canAddTransactionRequestToBeneficiary_(true) case _ => entity } @@ -1074,6 +1077,8 @@ object MapperViews extends Views with MdcLoggable { canSeeOtherAccountRoutingAddress_(true). canAddTransactionRequestToOwnAccount_(false). //added following two for payments canAddTransactionRequestToAnyAccount_(false). + canAddTransactionRequestToBeneficiary_(false). + canAddTransactionRequestToBeneficiary_(false). canSeeTransactionRequests_(false). canSeeTransactionRequestTypes_(false). canUpdateBankAccountLabel_(false). diff --git a/obp-api/src/main/scala/code/views/system/ViewDefinition.scala b/obp-api/src/main/scala/code/views/system/ViewDefinition.scala index 287f716ff9..879d3127ce 100644 --- a/obp-api/src/main/scala/code/views/system/ViewDefinition.scala +++ b/obp-api/src/main/scala/code/views/system/ViewDefinition.scala @@ -298,9 +298,15 @@ class ViewDefinition extends View with LongKeyedMapper[ViewDefinition] with Many } //internal transfer between my own accounts + + @deprecated("we added new field `canAddTransactionRequestToBeneficiary_`","25-07-2024") object canAddTransactionRequestToOwnAccount_ extends MappedBoolean(this){ override def defaultValue = false } + + object canAddTransactionRequestToBeneficiary_ extends MappedBoolean(this){ + override def defaultValue = false + } // transfer to any account object canAddTransactionRequestToAnyAccount_ extends MappedBoolean(this){ @@ -438,7 +444,7 @@ class ViewDefinition extends View with LongKeyedMapper[ViewDefinition] with Many canAddWhereTag_(actions.exists(_ == "can_add_where_tag")) canSeeWhereTag_(actions.exists(_ == "can_see_where_tag")) canDeleteWhereTag_(actions.exists(_ == "can_delete_where_tag")) - canAddTransactionRequestToOwnAccount_(actions.exists(_ == "can_add_transaction_request_to_own_account")) //added following two for payments + canAddTransactionRequestToBeneficiary_(actions.exists(_ == "can_add_transaction_request_to_beneficiary")) canAddTransactionRequestToAnyAccount_(actions.exists(_ == "can_add_transaction_request_to_any_account")) canSeeBankAccountCreditLimit_(actions.exists(_ == "can_see_bank_account_credit_limit")) canCreateDirectDebit_(actions.exists(_ == "can_create_direct_debit")) @@ -585,8 +591,9 @@ class ViewDefinition extends View with LongKeyedMapper[ViewDefinition] with Many def canSeeWhereTag : Boolean = canSeeWhereTag_.get def canDeleteWhereTag : Boolean = canDeleteWhereTag_.get - def canAddTransactionRequestToOwnAccount: Boolean = canAddTransactionRequestToOwnAccount_.get //added following two for payments + def canAddTransactionRequestToOwnAccount: Boolean = false //we do not need this field, set this to false. def canAddTransactionRequestToAnyAccount: Boolean = canAddTransactionRequestToAnyAccount_.get + def canAddTransactionRequestToBeneficiary: Boolean = canAddTransactionRequestToBeneficiary_.get def canSeeBankAccountCreditLimit: Boolean = canSeeBankAccountCreditLimit_.get def canCreateDirectDebit: Boolean = canCreateDirectDebit_.get diff --git a/obp-api/src/test/resources/frozen_type_meta_data b/obp-api/src/test/resources/frozen_type_meta_data index 3f67994246..a1a4db202b 100644 Binary files a/obp-api/src/test/resources/frozen_type_meta_data and b/obp-api/src/test/resources/frozen_type_meta_data differ diff --git a/obp-api/src/test/scala/code/api/v1_4_0/JSONFactory1_4_0_LightTest.scala b/obp-api/src/test/scala/code/api/v1_4_0/JSONFactory1_4_0_LightTest.scala index c49419e9ea..5ae2e0b554 100644 --- a/obp-api/src/test/scala/code/api/v1_4_0/JSONFactory1_4_0_LightTest.scala +++ b/obp-api/src/test/scala/code/api/v1_4_0/JSONFactory1_4_0_LightTest.scala @@ -104,11 +104,12 @@ class JSONFactory1_4_0_LightTest extends FeatureSpec listFields.toString contains ("private final java.util.Date code.api.v1_4_0.JSONFactory1_4_0_LightTest$ComplexNestedClass$1.complexNestedClassDate, ") shouldBe (true) listFields.toString contains ("private final scala.collection.immutable.List code.api.v1_4_0.JSONFactory1_4_0_LightTest$ClassTwo$1.strings2, ") shouldBe (true) listFields.toString contains ("private int java.lang.String.hash, private final java.lang.String code.api.v1_4_0.JSONFactory1_4_0_LightTest$ClassOne$1.string1, ") shouldBe (true) - listFields.toString contains ("private static final long java.lang.String.serialVersionUID, private final code.api.v1_4_0.JSONFactory1_4_0_LightTest code.api.v1_4_0.JSONFactory1_4_0_LightTest$ClassTwo$1.$outer, ") shouldBe (true) + listFields.toString contains ("private static final long java.lang.String.serialVersionUID,") shouldBe (true) + listFields.toString contains ("private final code.api.v1_4_0.JSONFactory1_4_0_LightTest code.api.v1_4_0.JSONFactory1_4_0_LightTest$ClassTwo$1.$outer, ") shouldBe (true) listFields.toString contains ("private final scala.Option code.api.v1_4_0.JSONFactory1_4_0_LightTest$ComplexNestedClass$1.complexNestedClassOptionNoneIn") shouldBe (true) // println(listFields) } - + } diff --git a/obp-api/src/test/scala/code/api/v4_0_0/CounterpartyTest.scala b/obp-api/src/test/scala/code/api/v4_0_0/CounterpartyTest.scala index 75007ee992..016bce8f2f 100644 --- a/obp-api/src/test/scala/code/api/v4_0_0/CounterpartyTest.scala +++ b/obp-api/src/test/scala/code/api/v4_0_0/CounterpartyTest.scala @@ -32,9 +32,9 @@ class CounterpartyTest extends V400ServerSetup { object ApiEndpoint5 extends Tag(nameOf(Implementations4_0_0.createExplicitCounterparty)) - object ApiEndpoint6 extends Tag(nameOf(Implementations4_0_0.getExplictCounterpartyById)) + object ApiEndpoint6 extends Tag(nameOf(Implementations4_0_0.getExplicitCounterpartyById)) object ApiEndpoint7 extends Tag(nameOf(Implementations4_0_0.deleteExplicitCounterparty)) - object ApiEndpoint8 extends Tag(nameOf(Implementations4_0_0.getExplictCounterpartiesForAccount)) + object ApiEndpoint8 extends Tag(nameOf(Implementations4_0_0.getExplicitCounterpartiesForAccount)) feature(s" test manage counterparties endpoints.") { diff --git a/obp-api/src/test/scala/code/api/v5_1_0/VRPConsentRequestTest.scala b/obp-api/src/test/scala/code/api/v5_1_0/VRPConsentRequestTest.scala index 7cdc044a57..4c548a674f 100644 --- a/obp-api/src/test/scala/code/api/v5_1_0/VRPConsentRequestTest.scala +++ b/obp-api/src/test/scala/code/api/v5_1_0/VRPConsentRequestTest.scala @@ -26,10 +26,13 @@ TESOBE (http://www.tesobe.com/) package code.api.v5_1_0 import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON +import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON.{accountRoutingJsonV121, bankRoutingJsonV121, branchRoutingJsonV141, postCounterpartyLimitV510} +import code.api.v5_0_0.ConsentJsonV500 import code.api.util.APIUtil.OAuth._ import code.api.util.ApiRole._ import code.api.util.Consent import code.api.util.ErrorMessages._ +import code.api.util.ExampleValue.counterpartyNameExample import code.api.v3_1_0.PostConsentChallengeJsonV310 import code.api.v4_0_0.OBPAPI4_0_0.Implementations4_0_0 import code.api.v4_0_0.UsersJsonV400 @@ -75,13 +78,29 @@ class VRPConsentRequestTest extends V510ServerSetup with PropsReset{ def createConsentByConsentRequestIdImplicit(requestId:String) = (v5_1_0_Request / "consumer"/ "consent-requests"/requestId/"IMPLICIT"/"consents").POST<@(user1) def getConsentByRequestIdUrl(requestId:String) = (v5_1_0_Request / "consumer"/ "consent-requests"/requestId/"consents").GET<@(user1) - lazy val postConsentRequestJson = SwaggerDefinitionsJSON.postConsentRequestJsonV510 - - + val fromAccountJson = ConsentRequestFromAccountJson ( + bank_routing = bankRoutingJsonV121.copy(address = testBankId1.value), + account_routing = accountRoutingJsonV121.copy(address = testAccountId0.value), + branch_routing = branchRoutingJsonV141 + ) + + val toAccountJson = ConsentRequestToAccountJson ( + counterparty_name = counterpartyNameExample.value, + bank_routing = bankRoutingJsonV121.copy(address = testBankId1.value), + account_routing = accountRoutingJsonV121.copy(address = testAccountId1.value), + branch_routing = branchRoutingJsonV141, + limit = postCounterpartyLimitV510 + ) + lazy val postVRPConsentRequestJson = SwaggerDefinitionsJSON.postVRPConsentRequestJsonV510.copy( + from_account=fromAccountJson, + to_account=toAccountJson + ) + + feature("Create/Get Consent Request v5.1.0") { scenario("We will call the Create endpoint without a user credentials", ApiEndpoint1, VersionOfApi) { When("We make a request v5.1.0") - val response510 = makePostRequest(createVRPConsentRequestWithoutLoginUrl, write(postConsentRequestJson)) + val response510 = makePostRequest(createVRPConsentRequestWithoutLoginUrl, write(postVRPConsentRequestJson)) Then("We should get a 401") response510.code should equal(401) response510.body.extract[ErrorMessage].message should equal (ApplicationNotIdentified) @@ -89,7 +108,7 @@ class VRPConsentRequestTest extends V510ServerSetup with PropsReset{ scenario("We will call the Create, Get and Delete endpoints with user credentials ", ApiEndpoint1, ApiEndpoint2, ApiEndpoint3, ApiEndpoint4, ApiEndpoint5, VersionOfApi) { When(s"We try $ApiEndpoint1 v5.1.0") - val createConsentResponse = makePostRequest(createVRPConsentRequestUrl, write(postConsentRequestJson)) + val createConsentResponse = makePostRequest(createVRPConsentRequestUrl, write(postVRPConsentRequestJson)) Then("We should get a 201") createConsentResponse.code should equal(201) val createConsentRequestResponseJson = createConsentResponse.body.extract[ConsentRequestResponseJson] @@ -108,9 +127,14 @@ class VRPConsentRequestTest extends V510ServerSetup with PropsReset{ val createConsentByRequestResponse = makePostRequest(createConsentByConsentRequestIdEmail(consentRequestId), write("")) Then("We should get a 200") createConsentByRequestResponse.code should equal(201) - val consentId = createConsentByRequestResponse.body.extract[ConsentJsonV510].consent_id - val consentJwt = createConsentByRequestResponse.body.extract[ConsentJsonV510].jwt - + val consentId = createConsentByRequestResponse.body.extract[ConsentJsonV500].consent_id + val consentJwt = createConsentByRequestResponse.body.extract[ConsentJsonV500].jwt + val accountAccess = createConsentByRequestResponse.body.extract[ConsentJsonV500].account_access + accountAccess.isDefined should equal(true) + accountAccess.get.bank_id should equal(fromAccountJson.bank_routing.address) + accountAccess.get.account_id should equal(fromAccountJson.account_routing.address) + accountAccess.get.view_id contains("_vrp-") shouldBe( true) + setPropsValues("consumer_validation_method_for_consent"->"NONE") val requestWhichFails = (v5_1_0_Request / "users").GET val responseWhichFails = makeGetRequest(requestWhichFails, List((s"Consent-JWT", consentJwt))) @@ -141,7 +165,7 @@ class VRPConsentRequestTest extends V510ServerSetup with PropsReset{ scenario("We will call the Create (IMPLICIT), Get and Delete endpoints with user credentials ", ApiEndpoint1, ApiEndpoint2, ApiEndpoint3, ApiEndpoint4, ApiEndpoint5, ApiEndpoint6, VersionOfApi) { When(s"We try $ApiEndpoint1 v5.1.0") - val createConsentResponse = makePostRequest(createVRPConsentRequestUrl, write(postConsentRequestJson)) + val createConsentResponse = makePostRequest(createVRPConsentRequestUrl, write(postVRPConsentRequestJson)) Then("We should get a 201") createConsentResponse.code should equal(201) val createConsentRequestResponseJson = createConsentResponse.body.extract[ConsentRequestResponseJson] @@ -160,8 +184,14 @@ class VRPConsentRequestTest extends V510ServerSetup with PropsReset{ val createConsentByRequestResponse = makePostRequest(createConsentByConsentRequestIdImplicit(consentRequestId), write("")) Then("We should get a 200") createConsentByRequestResponse.code should equal(201) - val consentId = createConsentByRequestResponse.body.extract[ConsentJsonV510].consent_id - val consentJwt = createConsentByRequestResponse.body.extract[ConsentJsonV510].jwt + val consentId = createConsentByRequestResponse.body.extract[ConsentJsonV500].consent_id + val consentJwt = createConsentByRequestResponse.body.extract[ConsentJsonV500].jwt + val accountAccess = createConsentByRequestResponse.body.extract[ConsentJsonV500].account_access + accountAccess.isDefined should equal(true) + accountAccess.get.bank_id should equal(fromAccountJson.bank_routing.address) + accountAccess.get.account_id should equal(fromAccountJson.account_routing.address) + accountAccess.get.view_id contains("_vrp-") shouldBe( true) + setPropsValues("consumer_validation_method_for_consent"->"NONE") val requestWhichFails = (v5_1_0_Request / "users").GET diff --git a/obp-api/src/test/scala/code/connector/RestConnector_vMar2019_frozen_meta_data b/obp-api/src/test/scala/code/connector/RestConnector_vMar2019_frozen_meta_data index 58c888c9b5..a18469eb1a 100644 Binary files a/obp-api/src/test/scala/code/connector/RestConnector_vMar2019_frozen_meta_data and b/obp-api/src/test/scala/code/connector/RestConnector_vMar2019_frozen_meta_data differ diff --git a/obp-api/src/test/scala/code/setup/TestConnectorSetupWithStandardPermissions.scala b/obp-api/src/test/scala/code/setup/TestConnectorSetupWithStandardPermissions.scala index dcc67510b9..511ec4b149 100644 --- a/obp-api/src/test/scala/code/setup/TestConnectorSetupWithStandardPermissions.scala +++ b/obp-api/src/test/scala/code/setup/TestConnectorSetupWithStandardPermissions.scala @@ -130,6 +130,7 @@ trait TestConnectorSetupWithStandardPermissions extends TestConnectorSetup { canSeeOtherAccountRoutingAddress_(true). canAddTransactionRequestToOwnAccount_(false). //added following two for payments canAddTransactionRequestToAnyAccount_(false). + canAddTransactionRequestToBeneficiary_(false). canSeeBankAccountCreditLimit_(true). saveMe } diff --git a/obp-api/src/test/scala/code/util/FrozenClassTest.scala b/obp-api/src/test/scala/code/util/FrozenClassTest.scala index a529ff9360..a7a7a5b7fd 100644 --- a/obp-api/src/test/scala/code/util/FrozenClassTest.scala +++ b/obp-api/src/test/scala/code/util/FrozenClassTest.scala @@ -53,7 +53,7 @@ class FrozenClassTest extends ServerSetup { } yield { s"$version increased apis: $increasedApis" } - increasedApis should equal(Nil) + increasedApis should equal(Nil) } } diff --git a/obp-commons/src/main/scala/com/openbankproject/commons/dto/JsonsTransfer.scala b/obp-commons/src/main/scala/com/openbankproject/commons/dto/JsonsTransfer.scala index d80578a5d6..916be6c834 100644 --- a/obp-commons/src/main/scala/com/openbankproject/commons/dto/JsonsTransfer.scala +++ b/obp-commons/src/main/scala/com/openbankproject/commons/dto/JsonsTransfer.scala @@ -1431,6 +1431,9 @@ case class InBoundGetChallengesByTransactionRequestId(inboundAdapterCallContext: case class OutBoundGetChallengesByConsentId(outboundAdapterCallContext: OutboundAdapterCallContext, consentId: String) extends TopicTrait case class InBoundGetChallengesByConsentId(inboundAdapterCallContext: InboundAdapterCallContext, status: Status, data: List[ChallengeCommons]) extends InBoundTrait[List[ChallengeCommons]] +case class OutBoundGetChallengesByBasketId(outboundAdapterCallContext: OutboundAdapterCallContext, basketId: String) extends TopicTrait +case class InBoundGetChallengesByBasketId(inboundAdapterCallContext: InboundAdapterCallContext, status: Status, data: List[ChallengeCommons]) extends InBoundTrait[List[ChallengeCommons]] + case class OutBoundGetCounterpartyByIbanAndBankAccountId(outboundAdapterCallContext: OutboundAdapterCallContext, iban: String, bankId: BankId, accountId: AccountId) extends TopicTrait case class InBoundGetCounterpartyByIbanAndBankAccountId(inboundAdapterCallContext: InboundAdapterCallContext, status: Status, data: CounterpartyTraitCommons) extends InBoundTrait[CounterpartyTraitCommons] diff --git a/obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModel.scala b/obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModel.scala index 3b869c13b9..b66518b83f 100644 --- a/obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModel.scala +++ b/obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModel.scala @@ -886,6 +886,7 @@ case class OutboundAdapterCallContext( consumerId: Option[String] = None, generalContext: Option[List[BasicGeneralContext]]= None, outboundAdapterAuthInfo: Option[OutboundAdapterAuthInfo] = None, + outboundAdapterConsenterInfo: Option[OutboundAdapterAuthInfo] = None, //Here consentInfo object structure is the same as AuthInfo. so share the same class ) case class BasicGeneralContext( diff --git a/obp-commons/src/main/scala/com/openbankproject/commons/model/ViewModel.scala b/obp-commons/src/main/scala/com/openbankproject/commons/model/ViewModel.scala index 4cf6b06fbb..a4b0fcf9f2 100644 --- a/obp-commons/src/main/scala/com/openbankproject/commons/model/ViewModel.scala +++ b/obp-commons/src/main/scala/com/openbankproject/commons/model/ViewModel.scala @@ -421,6 +421,7 @@ trait View { def canAddTransactionRequestToOwnAccount: Boolean //added following two for payments def canAddTransactionRequestToAnyAccount: Boolean + def canAddTransactionRequestToBeneficiary: Boolean def canSeeBankAccountCreditLimit: Boolean diff --git a/obp-commons/src/main/scala/com/openbankproject/commons/model/enums/Enumerations.scala b/obp-commons/src/main/scala/com/openbankproject/commons/model/enums/Enumerations.scala index b23ae9b95c..38a604467b 100644 --- a/obp-commons/src/main/scala/com/openbankproject/commons/model/enums/Enumerations.scala +++ b/obp-commons/src/main/scala/com/openbankproject/commons/model/enums/Enumerations.scala @@ -218,6 +218,11 @@ object AttributeType extends OBPEnumeration[AttributeType]{ object DATE_WITH_DAY extends Value } +sealed trait ConsentType extends EnumValue +object ConsentType extends OBPEnumeration[ConsentType]{ + object VRP extends Value +} + sealed trait AttributeCategory extends EnumValue object AttributeCategory extends OBPEnumeration[AttributeCategory]{ object Bank extends Value