From d3a021727276bf78da850b64b8799fb45b5a8395 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Tue, 26 Mar 2019 10:59:36 +0100 Subject: [PATCH] Consent endpoints - DONE --- .../src/main/scala/code/api/util/ApiTag.scala | 1 + .../main/scala/code/api/util/NewStyle.scala | 3 +- .../scala/code/api/v3_1_0/APIMethods310.scala | 61 ++++++++++++++++--- .../code/api/v3_1_0/JSONFactory3.1.0.scala | 5 +- .../scala/code/api/v3_1_0/OBPAPI3_1_0.scala | 1 + .../scala/code/consent/ConsentProvider.scala | 3 +- .../scala/code/consent/MappedConsent.scala | 21 ++++++- 7 files changed, 82 insertions(+), 13 deletions(-) diff --git a/obp-api/src/main/scala/code/api/util/ApiTag.scala b/obp-api/src/main/scala/code/api/util/ApiTag.scala index 89fbf0a92..ecbeee987 100644 --- a/obp-api/src/main/scala/code/api/util/ApiTag.scala +++ b/obp-api/src/main/scala/code/api/util/ApiTag.scala @@ -55,6 +55,7 @@ object ApiTag { val apiTagNewStyle = ResourceDocTag("New-Style") val apiTagWebhook = ResourceDocTag("Webhook") val apiTagMockedData = ResourceDocTag("Mocked-Data") + val apiTagConsent = ResourceDocTag("Consent") //Note: the followings are for the code generator -- UKOpenBankingV3.1.0 diff --git a/obp-api/src/main/scala/code/api/util/NewStyle.scala b/obp-api/src/main/scala/code/api/util/NewStyle.scala index 1619d1d66..f42c83f57 100644 --- a/obp-api/src/main/scala/code/api/util/NewStyle.scala +++ b/obp-api/src/main/scala/code/api/util/NewStyle.scala @@ -163,7 +163,8 @@ object NewStyle { (nameOf(Implementations3_1_0.createAccountAttribute), ApiVersion.v3_1_0.toString), (nameOf(Implementations3_1_0.deleteBranch), ApiVersion.v3_1_0.toString), (nameOf(Implementations3_1_0.getServerJWK), ApiVersion.v3_1_0.toString), - (nameOf(Implementations3_1_0.createConsent), ApiVersion.v3_1_0.toString) + (nameOf(Implementations3_1_0.createConsent), ApiVersion.v3_1_0.toString), + (nameOf(Implementations3_1_0.answerConsentChallenge), ApiVersion.v3_1_0.toString) ) object HttpCode { diff --git a/obp-api/src/main/scala/code/api/v3_1_0/APIMethods310.scala b/obp-api/src/main/scala/code/api/v3_1_0/APIMethods310.scala index e30c62432..6ecaf1224 100644 --- a/obp-api/src/main/scala/code/api/v3_1_0/APIMethods310.scala +++ b/obp-api/src/main/scala/code/api/v3_1_0/APIMethods310.scala @@ -3145,10 +3145,14 @@ trait APIMethods310 { |Create consent |""", PostConsentRequestJsonV310(email = "marko@tesobe.com", `for`="ALL_MY_ACCOUNTS", view="owner"), - ConsentRequestJsonV310(consent_id = "eyJhbGciOiJIUzI1NiJ9.eyJlbnRpdGxlbWVudHMiOltdLCJjcmVhdGVkQnlVc2VySWQiOiJhYjY1MzlhOS1iMTA1LTQ0ODktYTg4My0wYWQ4ZDZjNjE2NTciLCJzdWIiOiIyMWUxYzhjYy1mOTE4LTRlYWMtYjhlMy01ZTVlZWM2YjNiNGIiLCJhdWQiOiJlanpuazUwNWQxMzJyeW9tbmhieDFxbXRvaHVyYnNiYjBraWphanNrIiwibmJmIjoxNTUzNTU0ODk5LCJpc3MiOiJodHRwczpcL1wvd3d3Lm9wZW5iYW5rcHJvamVjdC5jb20iLCJleHAiOjE1NTM1NTg0OTksImlhdCI6MTU1MzU1NDg5OSwianRpIjoiMDlmODhkNWYtZWNlNi00Mzk4LThlOTktNjYxMWZhMWNkYmQ1Iiwidmlld3MiOlt7ImFjY291bnRfaWQiOiJtYXJrb19wcml2aXRlXzAxIiwiYmFua19pZCI6ImdoLjI5LnVrLngiLCJ2aWV3X2lkIjoib3duZXIifSx7ImFjY291bnRfaWQiOiJtYXJrb19wcml2aXRlXzAyIiwiYmFua19pZCI6ImdoLjI5LnVrLngiLCJ2aWV3X2lkIjoib3duZXIifV19.8cc7cBEf2NyQvJoukBCmDLT7LXYcuzTcSYLqSpbxLp4"), + ConsentRequestJsonV310( + consent_id = "9d429899-24f5-42c8-8565-943ffa6a7945", + jwt = "eyJhbGciOiJIUzI1NiJ9.eyJlbnRpdGxlbWVudHMiOltdLCJjcmVhdGVkQnlVc2VySWQiOiJhYjY1MzlhOS1iMTA1LTQ0ODktYTg4My0wYWQ4ZDZjNjE2NTciLCJzdWIiOiIyMWUxYzhjYy1mOTE4LTRlYWMtYjhlMy01ZTVlZWM2YjNiNGIiLCJhdWQiOiJlanpuazUwNWQxMzJyeW9tbmhieDFxbXRvaHVyYnNiYjBraWphanNrIiwibmJmIjoxNTUzNTU0ODk5LCJpc3MiOiJodHRwczpcL1wvd3d3Lm9wZW5iYW5rcHJvamVjdC5jb20iLCJleHAiOjE1NTM1NTg0OTksImlhdCI6MTU1MzU1NDg5OSwianRpIjoiMDlmODhkNWYtZWNlNi00Mzk4LThlOTktNjYxMWZhMWNkYmQ1Iiwidmlld3MiOlt7ImFjY291bnRfaWQiOiJtYXJrb19wcml2aXRlXzAxIiwiYmFua19pZCI6ImdoLjI5LnVrLngiLCJ2aWV3X2lkIjoib3duZXIifSx7ImFjY291bnRfaWQiOiJtYXJrb19wcml2aXRlXzAyIiwiYmFua19pZCI6ImdoLjI5LnVrLngiLCJ2aWV3X2lkIjoib3duZXIifV19.8cc7cBEf2NyQvJoukBCmDLT7LXYcuzTcSYLqSpbxLp4", + status = "INITIATED" + ), List(UnknownError), Catalogs(Core, notPSD2, OBWG), - apiTagCustomer :: apiTagNewStyle :: Nil) + apiTagConsent :: apiTagNewStyle :: Nil) lazy val createConsent : OBPEndpoint = { case "banks" :: BankId(bankId) :: "my" :: "consent" :: sca_method :: Nil JsonPost json -> _ => { @@ -3163,26 +3167,67 @@ trait APIMethods310 { consentJson <- NewStyle.function.tryons(failMsg, 400, callContext) { json.extract[PostConsentRequestJsonV310] } - consent <- Future(Consents.consentProvider.vend.createConsent()) map { + createdConsent <- Future(Consents.consentProvider.vend.createConsent()) map { i => connectorEmptyResponse(i, callContext) } - consentJWT = Consent.createConsentJWT(user, consentJson.view, consent.secret, consent.consentId) - _ <- Future(Consents.consentProvider.vend.updateConsent(consent.consentId, consentJWT)) map { + consentJWT = Consent.createConsentJWT(user, consentJson.view, createdConsent.secret, createdConsent.consentId) + _ <- Future(Consents.consentProvider.vend.setJsonWebToken(createdConsent.consentId, consentJWT)) map { i => connectorEmptyResponse(i, callContext) } } yield { sca_method match { case "email" => // Send the email - val params = PlainMailBodyType(consent.challenge) :: List(To(consentJson.email)) + val params = PlainMailBodyType(createdConsent.challenge) :: List(To(consentJson.email)) Mailer.sendMail( - From("challenge@tesobe.com"), + //From("challenge@tesobe.com"), + From("marko.milic@yahoo.com"), Subject("Challenge request"), params :_* ) case "sms" => case _ => } - (ConsentRequestJsonV310(consentJWT), HttpCode.`201`(callContext)) + (ConsentRequestJsonV310(createdConsent.consentId, consentJWT, createdConsent.status), HttpCode.`201`(callContext)) + } + } + } + + + resourceDocs += ResourceDoc( + answerConsentChallenge, + implementedInApiVersion, + nameOf(answerConsentChallenge), + "POST", + "/banks/BANK_ID/consent/CONSENT_ID/challenge", + "Answer Consent Challenge", + s""" + |Answer consent challenge + |""", + PostConsentChallengeJsonV310(answer = "12345678"), + ConsentChallengeJsonV310( + consent_id = "9d429899-24f5-42c8-8565-943ffa6a7945", + jwt = "eyJhbGciOiJIUzI1NiJ9.eyJlbnRpdGxlbWVudHMiOltdLCJjcmVhdGVkQnlVc2VySWQiOiJhYjY1MzlhOS1iMTA1LTQ0ODktYTg4My0wYWQ4ZDZjNjE2NTciLCJzdWIiOiIyMWUxYzhjYy1mOTE4LTRlYWMtYjhlMy01ZTVlZWM2YjNiNGIiLCJhdWQiOiJlanpuazUwNWQxMzJyeW9tbmhieDFxbXRvaHVyYnNiYjBraWphanNrIiwibmJmIjoxNTUzNTU0ODk5LCJpc3MiOiJodHRwczpcL1wvd3d3Lm9wZW5iYW5rcHJvamVjdC5jb20iLCJleHAiOjE1NTM1NTg0OTksImlhdCI6MTU1MzU1NDg5OSwianRpIjoiMDlmODhkNWYtZWNlNi00Mzk4LThlOTktNjYxMWZhMWNkYmQ1Iiwidmlld3MiOlt7ImFjY291bnRfaWQiOiJtYXJrb19wcml2aXRlXzAxIiwiYmFua19pZCI6ImdoLjI5LnVrLngiLCJ2aWV3X2lkIjoib3duZXIifSx7ImFjY291bnRfaWQiOiJtYXJrb19wcml2aXRlXzAyIiwiYmFua19pZCI6ImdoLjI5LnVrLngiLCJ2aWV3X2lkIjoib3duZXIifV19.8cc7cBEf2NyQvJoukBCmDLT7LXYcuzTcSYLqSpbxLp4", + status = "INITIATED" + ), + List(UnknownError), + Catalogs(Core, notPSD2, OBWG), + apiTagConsent :: apiTagNewStyle :: Nil) + + lazy val answerConsentChallenge : OBPEndpoint = { + case "banks" :: BankId(bankId) :: "consent" :: consentId :: "challenge" :: Nil JsonPost json -> _ => { + cc => + for { + (_, callContext) <- authorizedAccess(cc) + (_, callContext) <- NewStyle.function.getBank(bankId, callContext) + failMsg = s"$InvalidJsonFormat The Json body should be the $PostConsentChallengeJsonV310 " + consentJson <- NewStyle.function.tryons(failMsg, 400, callContext) { + json.extract[PostConsentChallengeJsonV310] + } + consent <- Future(Consents.consentProvider.vend.checkAnswer(consentId, consentJson.answer)) map { + i => connectorEmptyResponse(i, callContext) + } + } yield { + (ConsentRequestJsonV310(consent.consentId, consent.jsonWebToken, consent.status), HttpCode.`201`(callContext)) } } } diff --git a/obp-api/src/main/scala/code/api/v3_1_0/JSONFactory3.1.0.scala b/obp-api/src/main/scala/code/api/v3_1_0/JSONFactory3.1.0.scala index cab498840..b0ff6472f 100644 --- a/obp-api/src/main/scala/code/api/v3_1_0/JSONFactory3.1.0.scala +++ b/obp-api/src/main/scala/code/api/v3_1_0/JSONFactory3.1.0.scala @@ -472,7 +472,10 @@ case class MeetingsJsonV310( ) case class PostConsentRequestJsonV310(email: String, `for`: String, view: String) -case class ConsentRequestJsonV310(consent_id: String) +case class ConsentRequestJsonV310(consent_id: String, jwt: String, status: String) + +case class PostConsentChallengeJsonV310(answer: String) +case class ConsentChallengeJsonV310(consent_id: String, jwt: String, status: String) object JSONFactory310{ def createCheckbookOrdersJson(checkbookOrders: CheckbookOrdersJson): CheckbookOrdersJson = 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 c724371e8..f134f35e5 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 @@ -337,6 +337,7 @@ object OBPAPI3_1_0 extends OBPRestHelper with APIMethods130 with APIMethods140 w Implementations3_1_0.getMeeting :: Implementations3_1_0.getServerJWK :: Implementations3_1_0.createConsent :: + Implementations3_1_0.answerConsentChallenge :: Nil val allResourceDocs = Implementations3_1_0.resourceDocs ++ diff --git a/obp-api/src/main/scala/code/consent/ConsentProvider.scala b/obp-api/src/main/scala/code/consent/ConsentProvider.scala index d3e09a271..2c9e0e8be 100644 --- a/obp-api/src/main/scala/code/consent/ConsentProvider.scala +++ b/obp-api/src/main/scala/code/consent/ConsentProvider.scala @@ -11,7 +11,8 @@ object Consents extends SimpleInjector { trait ConsentProvider { def getConsentByConsentId(consentId: String): Box[MappedConsent] def createConsent(): Box[MappedConsent] - def updateConsent(consentId: String, jwt: String): Box[MappedConsent] + def setJsonWebToken(consentId: String, jwt: String): Box[MappedConsent] + def checkAnswer(consentId: String, challenge: String): Box[MappedConsent] } trait Consent { diff --git a/obp-api/src/main/scala/code/consent/MappedConsent.scala b/obp-api/src/main/scala/code/consent/MappedConsent.scala index 05dd69586..121ec8c38 100644 --- a/obp-api/src/main/scala/code/consent/MappedConsent.scala +++ b/obp-api/src/main/scala/code/consent/MappedConsent.scala @@ -22,12 +22,11 @@ object MappedConsentProvider extends ConsentProvider { .saveMe() } } - override def updateConsent(consentId: String, jwt: String): Box[MappedConsent] = { + override def setJsonWebToken(consentId: String, jwt: String): Box[MappedConsent] = { MappedConsent.find(By(MappedConsent.mConsentId, consentId)) match { case Full(consent) => tryo(consent .mJsonWebToken(jwt) - .mConsentId(consentId) .saveMe()) case Empty => Empty ?~! ErrorMessages.ConsentNotFound @@ -35,6 +34,24 @@ object MappedConsentProvider extends ConsentProvider { Failure(msg) case _ => Failure(ErrorMessages.UnknownError) + } + } + override def checkAnswer(consentId: String, challenge: String): Box[MappedConsent] = { + MappedConsent.find(By(MappedConsent.mConsentId, consentId)) match { + case Full(consent) => + consent.status match { + case value if value == ConsentStatus.INITIATED.toString => + val status = if (consent.challenge == challenge) ConsentStatus.ACCEPTED.toString else ConsentStatus.REJECTED.toString + tryo(consent.mStatus(status).saveMe()) + case _ => + Full(consent) + } + case Empty => + Empty ?~! ErrorMessages.ConsentNotFound + case Failure(msg, _, _) => + Failure(msg) + case _ => + Failure(ErrorMessages.UnknownError) } }