Merge pull request #2489 from constantine2nd/develop

Enable customer identification via TPP-Signature-Certificate
This commit is contained in:
Simon Redfern 2025-02-11 15:10:03 +01:00 committed by GitHub
commit 1a083c50d2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 27 additions and 8 deletions

View File

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

View File

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

View File

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

View File

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