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 55adae901..5040e26b9 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 @@ -664,7 +664,7 @@ where the consent was directly managed between ASPSP and PSU e.g. in a re-direct "lastActionDate": "2019-06-30", "consentStatus": "received" }"""), - List(UserNotLoggedIn, UnknownError), + List(UserNotLoggedIn, ConsentNotFound, UnknownError), ApiTag("Account Information Service (AIS)") :: apiTagBerlinGroupM :: Nil ) @@ -672,11 +672,14 @@ where the consent was directly managed between ASPSP and PSU e.g. in a re-direct case "consents" :: consentId :: Nil JsonGet _ => { cc => for { - (Full(u), callContext) <- authenticatedAccess(cc) + (_, callContext) <- applicationAccess(cc) _ <- passesPsd2Aisp(callContext) consent <- Future(Consents.consentProvider.vend.getConsentByConsentId(consentId)) map { unboxFullOrFail(_, callContext, s"$ConsentNotFound ($consentId)") - } + } + _ <- Helper.booleanToFuture(failMsg = s"${consent.mConsumerId.get} != ${cc.consumer.map(_.consumerId.get).getOrElse("None")}", failCode = 404, cc = cc.callContext) { + consent.mConsumerId.get == callContext.map(_.consumer.map(_.consumerId.get).getOrElse("None")).getOrElse("None") + } } yield { (createGetConsentResponseJson(consent), HttpCode.`200`(callContext)) } diff --git a/obp-api/src/main/scala/code/api/constant/constant.scala b/obp-api/src/main/scala/code/api/constant/constant.scala index 07c0bd025..cbb9259ed 100644 --- a/obp-api/src/main/scala/code/api/constant/constant.scala +++ b/obp-api/src/main/scala/code/api/constant/constant.scala @@ -144,7 +144,9 @@ object RequestHeader { final lazy val `Consent-JWT` = "Consent-JWT" final lazy val `PSD2-CERT` = "PSD2-CERT" final lazy val `If-None-Match` = "If-None-Match" - final lazy val `TPP-Redirect-URL` = "TPP-Redirect-URL" + final lazy val `TPP-Redirect-URL` = "TPP-Redirect-URL" // Berlin Group + final lazy val `TPP-Signature-Certificate` = "TPP-Signature-Certificate" // Berlin Group + final lazy val `X-Request-ID` = "X-Request-ID" // Berlin Group /** * The If-Modified-Since request HTTP header makes the request conditional: * the server sends back the requested resource, with a 200 status, 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 e394d1a78..05c4595d0 100644 --- a/obp-api/src/main/scala/code/api/util/APIUtil.scala +++ b/obp-api/src/main/scala/code/api/util/APIUtil.scala @@ -255,6 +255,16 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{ case _ => None } } + /** + * Purpose of this helper function is to get the PSD2-CERT value from a Request Headers. + * @return the PSD2-CERT value from a Request Header as a String + */ + def getTppSignatureCertificate(requestHeaders: List[HTTPParam]): Option[String] = { + requestHeaders.toSet.filter(_.name == RequestHeader.`TPP-Signature-Certificate`).toList match { + case x :: Nil => Some(x.values.mkString(", ")) + case _ => None + } + } def getRequestHeader(name: String, requestHeaders: List[HTTPParam]): String = { requestHeaders.toSet.filter(_.name.toLowerCase == name.toLowerCase).toList match { @@ -2983,6 +2993,9 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{ val authHeaders = AuthorisationUtil.getAuthorisationHeaders(reqHeaders) + // Identify consumer via certificate + val consumerByCertificate = Consent.getCurrentConsumerViaMtls(callContext = cc) + val res = if (authHeaders.size > 1) { // Check Authorization Headers ambiguity Future { (Failure(ErrorMessages.AuthorizationHeaderAmbiguity + s"${authHeaders}"), None) } @@ -3109,7 +3122,7 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{ // - Authorization: Basic mF_9.B5f-4.1JqM Future { (Failure(ErrorMessages.InvalidAuthorizationHeader), Some(cc)) } } else { - Future { (Empty, Some(cc)) } + Future { (Empty, Some(cc.copy(consumer = consumerByCertificate))) } } } 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 ab6c2b377..20e423cba 100644 --- a/obp-api/src/main/scala/code/api/util/ConsentUtil.scala +++ b/obp-api/src/main/scala/code/api/util/ConsentUtil.scala @@ -128,9 +128,10 @@ object Consent extends MdcLoggable { * @return the boxed Consumer */ def getCurrentConsumerViaMtls(callContext: CallContext): Box[Consumer] = { - val clientCert: String = APIUtil.`getPSD2-CERT`(callContext.requestHeaders) - .getOrElse(SecureRandomUtil.csprng.nextLong().toString) - + val clientCert: String = APIUtil.`getPSD2-CERT`(callContext.requestHeaders) // MTLS certificate QWAC (Qualified Website Authentication Certificate) + .orElse(APIUtil.getTppSignatureCertificate(callContext.requestHeaders)) // Signature certificate QSealC (Qualified Electronic Seal Certificate) + .getOrElse(SecureRandomUtil.csprng.nextLong().toString) // Force to fail + { // 1st search is via the original value logger.debug(s"getConsumerByPemCertificate ${clientCert}") Consumers.consumers.vend.getConsumerByPemCertificate(clientCert)