Skip to content

Commit aacaa36

Browse files
authored
Merge pull request #2518 from constantine2nd/develop
Berlin Group features
2 parents 7d05d81 + 5e184c0 commit aacaa36

25 files changed

Lines changed: 329 additions & 121 deletions

obp-api/src/main/resources/props/sample.props.template

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,12 @@ jwt.use.ssl=false
165165
# Bypass TPP signature validation
166166
# bypass_tpp_signature_validation = false
167167

168-
## Reject Berlin Group Consents in status "received" after defined time
169-
# berlin_group_outdated_consents_interval = 5
168+
## Reject Berlin Group consents with status "received" after a defined time (in seconds)
169+
# berlin_group_outdated_consents_time_in_seconds = 300
170+
# berlin_group_outdated_consents_interval_in_seconds =
171+
172+
## Expire Berlin Group consents with status "valid"
173+
# berlin_group_expired_consents_interval_in_seconds =
170174

171175

172176
## Enable writing API metrics (which APIs are called) to RDBMS
@@ -1124,7 +1128,7 @@ default_auth_context_update_request_key=CUSTOMER_NUMBER
11241128
#berlin_group_error_message_show_path = true
11251129

11261130
# Check presence of the mandatory headers
1127-
#berlin_group_mandatory_headers = X-Request-ID,PSU-IP-Address,PSU-Device-ID,PSU-Device-Name
1131+
#berlin_group_mandatory_headers = Content-Type,Date,Digest,PSU-Device-ID,PSU-Device-Name,PSU-IP-Address,Signature,TPP-Signature-Certificate,X-Request-ID
11281132
#berlin_group_mandatory_header_consent = TPP-Redirect-URL
11291133

11301134
## Berlin Group Create Consent Frequency per Day Upper Limit

obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5508,6 +5508,9 @@ object SwaggerDefinitionsJSON {
55085508
val consumerLogoUrlJson = ConsumerLogoUrlJson(
55095509
"http://localhost:8888"
55105510
)
5511+
val consumerCertificateJson = ConsumerCertificateJson(
5512+
"QmFnIEF0dHJpYnV0ZXMNCiAgICBsb2NhbEtleUlEOiBFMSA3RiBCMyBCOCBEQiA4QyA2NCBGNiA4QyA1NSAzNCA3QSAyNiBCRSBEMCBCNCBENCBBMyBGRCA2NiANCnN1YmplY3Q9QyA9IE1ELCBPID0gTUFJQiwgQ04gPSBNQUlCIFByaXNhY2FydSBTZXJnaXUgKFRlc3QpDQoNCmlzc3Vlcj1DID0gTUQsIE8gPSBCTk0sIE9VID0gRFRJLCBDTiA9IEJOTSBDQSAodGVzdCksIGVtYWlsQWRkcmVzcyA9IGFkbWluQGJubS5tZA0KDQotLS0tLUJFR0lOIENFUlRJRklDQVRFLS0tLS0NCk1JSUdoVENDQkcyZ0F3SUJBZ0lDQkRvd0RRWUpLb1pJaHZjTkFRRUZCUUF3WGpFTE1Ba0dBMVVFQmhNQ1RVUXgNCkREQUtCZ05WQkFvTUEwSk9UVEVNTUFvR0ExVUVDd3dEUkZSSk1SWXdGQVlEVlFRRERBMUNUazBnUTBFZ0tIUmwNCmMzUXBNUnN3R1FZSktvWklodmNOQVFrQkZneGhaRzFwYmtCaWJtMHViV1F3SGhjTk1qUXdOREU0TVRFME5qUXgNCldoY05Nall3TkRFNE1URTBOalF4V2pCRE1Rc3dDUVlEVlFRR0V3Sk5SREVOTUFzR0ExVUVDZ3dFVFVGSlFqRWwNCk1DTUdBMVVFQXd3Y1RVRkpRaUJRY21sellXTmhjblVnVTJWeVoybDFJQ2hVWlhOMEtUQ0NBU0l3RFFZSktvWkkNCmh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTFdYMzlFSmZLNEg5MDZKSVpMbHRxTU56amxDd3NyMm0rZjMNCjVYdHZ4SVY1akEvUWlZSDdDVjBQK0E1U2grKytaNldUb1NnQStQemYwdTdWYWRVbWtyWEZBV0lzOXlPemduUjQNCmZ5TVVSNXR4UWJYdmZYcXVJUS9XQ0ZnRHBIU3I4eWN0UHlsOGdsUjFidVF0UmlTdEdMT0RnalhsTmhTMlhTYTMNCmFwVGhUVHAya3o1dEoyWjBXRnlxa1ZVM1FJNkdNVGU5eWhDdnVZQkI1QWJuUUU4bXVPb2NNaEJkRFREY2ZGdW0NCk5paUozelhLMXZzKzEzNW5sZEMxOXozWnBuaVBSeER2WGthR00wc0xiNnk5T1NIOUdmYTZHcXJnendTTmpubEkNCnZCeWFlK1dtbG16TzlBZXVKNVRaUFhPdzNwcFdpTWdTOVlZOWp1UUtFQUFBQUFBQTQ4c0NBd0VBQWFPQ0FtWXcNCmdnSmlNQkVHQ1dDR1NBR0crRUlCQVFRRUF3SUZvREFwQmdOVkhTVUVJakFnQmdnckJnRUZCUWNEQWdZSUt3WUINCkJRVUhBd1FHQ2lzR0FRUUJnamNVQWdJd0hRWURWUjBPQkJZRUZGR2ptcXM4OXUyMXcvZmNHVlgrb0pNZSsvWTYNCk1JR1FCZ05WSFNNRWdZZ3dnWVdBRkh1ckdvcWhWYVFUVkJwRVlObmNnRUl5Vkd3dG9XS2tZREJlTVFzd0NRWUQNClZRUUdFd0pOUkRFTU1Bb0dBMVVFQ2d3RFFrNU5NUXd3Q2dZRFZRUUxEQU5FVkVreEZqQVVCZ05WQkFNTURVSk8NClRTQkRRU0FvZEdWemRDa3hHekFaQmdrcWhraUc5dzBCQ1FFV0RHRmtiV2x1UUdKdWJTNXRaSUlKQUpuU0UxdVoNCkU1MU5NQlFHQTFVZEVnUU5NQXVCQ1VOQlFHSnViUzV0WkRBMkJnbGdoa2dCaHZoQ0FRUUVLUlluYUhSMGNEb3YNCkwzQnJhUzVpYm0wdWJXUXZjR3RwTDNCMVlpOWpjbXd2WTJGamNtd3VZM0pzTURZR0NXQ0dTQUdHK0VJQkF3UXANCkZpZG9kSFJ3T2k4dmNHdHBMbUp1YlM1dFpDOXdhMmt2Y0hWaUwyTnliQzlqWVdOeWJDNWpjbXd3T0FZRFZSMGYNCkJERXdMekF0b0N1Z0tZWW5hSFIwY0RvdkwzQnJhUzVpYm0wdWJXUXZjR3RwTDNCMVlpOWpjbXd2WTJGamNtd3UNClkzSnNNRU1HQ0NzR0FRVUZCd0VCQkRjd05UQXpCZ2dyQmdFRkJRY3dBb1luYUhSMGNEb3ZMM2QzZHk1aWJtMHUNCmJXUXZjSFZpTDJOaFkyVnlkQzlqWVdObGNuUXVZM0owTUZBR0NXQ0dTQUdHK0VJQkRRUkRGa0ZMWlhrZ1VHRnANCmNpQkhaVzVsY21GMFpXUWdZbmtnVlc1cFEzSjVjSFFnZGk0d0xqWXVPQzQySUdadmNpQlFTME5USXpFeUxXWnANCmJHVWdjM1J2Y21GblpUQUpCZ05WSFJNRUFqQUFNQTRHQTFVZER3RUIvd1FFQXdJR1FEQU5CZ2txaGtpRzl3MEINCkFRVUZBQU9DQWdFQUpTU0ZhRWZOOWVna2wyYVFEc3QvVEtWWmxSbFdWZWkrVmZwMnM1ZXpWNG9ibnZRUXI5QkcNCmZrNklqaU8zbGZHTjQyTkVZSTV6SGh3SDl2WTRiMjM2ZkdMZWltbmZDc2lGb0FyTEtGUDR6Y0dvS0ZJR2ZBNDINCnQzSmxIcENvbmNpMmxqUzg4MzN2c1k5M2xGSzFTa2NvUjBMT0s0NzdaNlBWMjVtdjVjdmhCN1ZkNWs4SWpLU3MNCllwWkpaSi9STWZNT3dPQUtqeDFhWDNxQUhhNVhTOUNINEJaMEl4SnBYcWZpMm5GUFVNRy8yU0JmSTN4dDhsM1UNClJtVy9qZVRoRG5tL0Vsb05sb3pObzdRS3AvbysyUVBFZDBUWkFBdUljQWFiM09waUptOWlrUlh3c21mNkFmS0INCnIwQmtHcTFiTi9RQk1DMDM4RHA4S1pKZmdmaTYxYnBiVUNFdDRsVWY0R252TW9FdjZnbTh1czE2VTI1d0Y0SUwNCnd5cmFBZHJUVHhaWEVydGY2c3pWY2JBRUY0QmdFM0hCVmF2V2FxbDZac1FFRFJoTGVtWVJwMHhleUtwYXI4d3INClhqN1oycmJteWpFci9ES1hMdHF2UlFIQVVrVDBEQXRST2R4NmpsNUtGSFVvbTM2QUZmeU5UcjJ6a0p2MkZWTlENCmc0TnJMRnk0WldidE84ZDc2M2NoMEpjaWYzZUdadnFmQnVETUs3Q25jUWluamxVcTg1cFpzeGlFUW56VTJOdGgNClRFUzBqZjZ6ZS9ibHpVaUsrRXlyeWpEeWNaYlk3RHlwWWVlTlJJbk9zVUVjZmtFT3BVL3dFTG83dnpNaGY1b2MNCmdjcUFKSzdOQWlEQzVHR0Iyb296ZzNSTTJBbGdPT1ZpRFZwRzRMaUxPenpqVStqaXlyclY3OGs9DQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tDQo="
5513+
)
55115514
val consumerNameJson = ConsumerNameJson(
55125515
"App name"
55135516
)

obp-api/src/main/scala/code/api/berlin/group/v1_3/AccountInformationServiceAISApi.scala

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -242,10 +242,10 @@ recurringIndicator:
242242
for {
243243
(Full(user), callContext) <- authenticatedAccess(cc)
244244
_ <- passesPsd2Aisp(callContext)
245-
consent <- Future(Consents.consentProvider.vend.getConsentByConsentId(consentId)) map {
245+
_ <- Future(Consents.consentProvider.vend.getConsentByConsentId(consentId)) map {
246246
unboxFullOrFail(_, callContext, ConsentNotFound)
247247
}
248-
consent <- Future(Consents.consentProvider.vend.revoke(consentId)) map {
248+
_ <- Future(Consents.consentProvider.vend.revokeBerlinGroupConsent(consentId)) map {
249249
i => connectorEmptyResponse(i, callContext)
250250
}
251251
} yield {
@@ -694,8 +694,10 @@ where the consent was directly managed between ASPSP and PSU e.g. in a re-direct
694694
consent <- Future(Consents.consentProvider.vend.getConsentByConsentId(consentId)) map {
695695
unboxFullOrFail(_, callContext, s"$ConsentNotFound ($consentId)")
696696
}
697-
_ <- Helper.booleanToFuture(failMsg = s"${consent.mConsumerId.get} != ${cc.consumer.map(_.consumerId.get).getOrElse("None")}", failCode = 404, cc = cc.callContext) {
698-
consent.mConsumerId.get == callContext.map(_.consumer.map(_.consumerId.get).getOrElse("None")).getOrElse("None")
697+
consumerIdFromConsent = consent.mConsumerId.get
698+
consumerIdFromCurrentCall = callContext.map(_.consumer.map(_.consumerId.get).getOrElse("None")).getOrElse("None")
699+
_ <- Helper.booleanToFuture(failMsg = s"$ConsentNotFound $consumerIdFromConsent != $consumerIdFromCurrentCall", failCode = 403, cc = cc.callContext) {
700+
consumerIdFromConsent == consumerIdFromCurrentCall
699701
}
700702
} yield {
701703
(createGetConsentResponseJson(consent), HttpCode.`200`(callContext))
@@ -767,8 +769,7 @@ This method returns the SCA status of a consent initiation's authorisation sub-r
767769
unboxFullOrFail(_, callContext, ConsentNotFound)
768770
}
769771
} yield {
770-
val status = consent.status.toLowerCase()
771-
.replace(ConsentStatus.REVOKED.toString.toLowerCase(), "revokedByPsu")
772+
val status = consent.status
772773
(JSONFactory_BERLIN_GROUP_1_3.ConsentStatusJsonV13(status), HttpCode.`200`(callContext))
773774
}
774775

obp-api/src/main/scala/code/api/berlin/group/v1_3/BgSpecValidation.scala

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,19 +48,19 @@ object BgSpecValidation {
4848
// Example usage
4949
def main(args: Array[String]): Unit = {
5050
val testDates = Seq(
51-
"2025-05-10", // More than 180 days ahead
52-
"9999-12-31", // Exceeds max allowed
53-
"2015-01-01", // In the past
54-
"invalid-date", // Invalid format
55-
LocalDate.now().plusDays(90).toString, // Valid (within 180 days)
56-
LocalDate.now().plusDays(180).toString, // Valid (exactly 180 days)
57-
LocalDate.now().plusDays(181).toString // More than 180 days
51+
"2025-05-10", // More than 180 days ahead
52+
"9999-12-31", // Exceeds max allowed
53+
"2015-01-01", // In the past
54+
"invalid-date", // Invalid format
55+
LocalDate.now().plusDays(90).toString, // Valid (within 180 days)
56+
LocalDate.now().plusDays(180).toString, // Valid (exactly 180 days)
57+
LocalDate.now().plusDays(181).toString // More than 180 days
5858
)
5959

6060
testDates.foreach { date =>
6161
validateValidUntil(date) match {
62-
case Right(validDate) => println(s"Valid date: $validDate")
63-
case Left(error) => println(s"Error: $error")
62+
case Right(validDate) => println(s"Valid date: $validDate")
63+
case Left(error) => println(s"Error: $error")
6464
}
6565
}
6666
}

obp-api/src/main/scala/code/api/util/APIUtil.scala

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -254,16 +254,6 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
254254
case _ => None
255255
}
256256
}
257-
/**
258-
* Purpose of this helper function is to get the PSD2-CERT value from a Request Headers.
259-
* @return the PSD2-CERT value from a Request Header as a String
260-
*/
261-
def getTppSignatureCertificate(requestHeaders: List[HTTPParam]): Option[String] = {
262-
requestHeaders.toSet.filter(_.name == RequestHeader.`TPP-Signature-Certificate`).toList match {
263-
case x :: Nil => Some(x.values.mkString(", "))
264-
case _ => None
265-
}
266-
}
267257

268258
def getRequestHeader(name: String, requestHeaders: List[HTTPParam]): String = {
269259
requestHeaders.toSet.filter(_.name.toLowerCase == name.toLowerCase).toList match {
@@ -527,22 +517,31 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
527517
*
528518
*/
529519
def getRequestHeadersToMirror(callContext: Option[CallContextLight]): CustomResponseHeaders = {
520+
val mirrorByProperties = getPropsValue("mirror_request_headers_to_response", "").split(",").toList.map(_.trim)
521+
530522
val mirrorRequestHeadersToResponse: List[String] =
531-
getPropsValue("mirror_request_headers_to_response", "").split(",").toList.map(_.trim)
523+
if (callContext.exists(_.url.contains(ApiVersion.berlinGroupV13.urlPrefix))) {
524+
// Berlin Group Specification
525+
RequestHeader.`X-Request-ID` :: mirrorByProperties
526+
} else {
527+
mirrorByProperties
528+
}
529+
532530
callContext match {
533531
case Some(cc) =>
534532
cc.requestHeaders match {
535533
case Nil => CustomResponseHeaders(Nil)
536534
case _ =>
537535
val headers = cc.requestHeaders
538536
.filter(item => mirrorRequestHeadersToResponse.contains(item.name))
539-
.map(item => (item.name, item.values.head))
537+
.map(item => (item.name, item.values.headOption.getOrElse(""))) // Safe extraction
540538
CustomResponseHeaders(headers)
541539
}
542540
case None =>
543541
CustomResponseHeaders(Nil)
544542
}
545543
}
544+
546545
/**
547546
*
548547
*/
@@ -2993,15 +2992,23 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
29932992
val remoteIpAddress = getRemoteIpAddress()
29942993

29952994
val authHeaders = AuthorisationUtil.getAuthorisationHeaders(reqHeaders)
2995+
val authHeadersWithEmptyValues = RequestHeadersUtil.checkEmptyRequestHeaderValues(reqHeaders)
2996+
val authHeadersWithEmptyNames = RequestHeadersUtil.checkEmptyRequestHeaderNames(reqHeaders)
29962997

29972998
// Identify consumer via certificate
2998-
val consumerByCertificate = Consent.getCurrentConsumerViaMtls(callContext = cc)
2999+
val consumerByCertificate = Consent.getCurrentConsumerViaTppSignatureCertOrMtls(callContext = cc)
29993000

30003001
val res =
3001-
if (authHeaders.size > 1) { // Check Authorization Headers ambiguity
3002+
if (authHeadersWithEmptyValues.nonEmpty) { // Check Authorization Headers Empty Values
3003+
val message = ErrorMessages.EmptyRequestHeaders + s"Header names: ${authHeadersWithEmptyValues.mkString(", ")}"
3004+
Future { (fullBoxOrException(Empty ~> APIFailureNewStyle(message, 400, Some(cc.toLight))), None) }
3005+
} else if (authHeadersWithEmptyNames.nonEmpty) { // Check Authorization Headers Empty Names
3006+
val message = ErrorMessages.EmptyRequestHeaders + s"Header values: ${authHeadersWithEmptyNames.mkString(", ")}"
3007+
Future { (fullBoxOrException(Empty ~> APIFailureNewStyle(message, 400, Some(cc.toLight))), None) }
3008+
} else if (authHeaders.size > 1) { // Check Authorization Headers ambiguity
30023009
Future { (Failure(ErrorMessages.AuthorizationHeaderAmbiguity + s"${authHeaders}"), None) }
30033010
} else if (APIUtil.`hasConsent-ID`(reqHeaders)) { // Berlin Group's Consent
3004-
Consent.applyBerlinGroupRules(APIUtil.`getConsent-ID`(reqHeaders), cc)
3011+
Consent.applyBerlinGroupRules(APIUtil.`getConsent-ID`(reqHeaders), cc.copy(consumer = consumerByCertificate))
30053012
} else if (APIUtil.hasConsentJWT(reqHeaders)) { // Open Bank Project's Consent
30063013
val consentValue = APIUtil.getConsentJWT(reqHeaders)
30073014
Consent.getConsentJwtValueByConsentId(consentValue.getOrElse("")) match {
@@ -3011,12 +3018,12 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
30113018
// Note: At this point we are getting the Consumer from the Consumer in the Consent.
30123019
// This may later be cross checked via the value in consumer_validation_method_for_consent.
30133020
// Get the source of truth for Consumer (e.g. CONSUMER_CERTIFICATE) as early as possible.
3014-
cc.copy(consumer = Consent.getCurrentConsumerViaMtls(callContext = cc))
3021+
cc.copy(consumer = consumerByCertificate)
30153022
)
30163023
case _ =>
30173024
JwtUtil.checkIfStringIsJWTValue(consentValue.getOrElse("")).isDefined match {
30183025
case true => // It's JWT obtained via "Consent-JWT" request header
3019-
Consent.applyRules(APIUtil.getConsentJWT(reqHeaders), cc)
3026+
Consent.applyRules(APIUtil.getConsentJWT(reqHeaders), cc.copy(consumer = consumerByCertificate))
30203027
case false => // Unrecognised consent value
30213028
Future { (Failure(ErrorMessages.ConsentHeaderValueInvalid), None) }
30223029
}
@@ -3115,8 +3122,7 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
31153122
}
31163123
else if(Option(cc).flatMap(_.user).isDefined) {
31173124
Future{(cc.user, Some(cc))}
3118-
}
3119-
else {
3125+
} else {
31203126
if(hasAuthorizationHeader(reqHeaders)) {
31213127
// We want to throw error in case of wrong or unsupported header. For instance:
31223128
// - Authorization: mF_9.B5f-4.1JqM

obp-api/src/main/scala/code/api/util/ApiRole.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,8 @@ object ApiRole extends MdcLoggable{
243243

244244
case class CanUpdateConsumerLogoUrl(requiresBankId: Boolean = false) extends ApiRole
245245
lazy val canUpdateConsumerLogoUrl = CanUpdateConsumerLogoUrl()
246+
case class CanUpdateConsumerCertificate(requiresBankId: Boolean = false) extends ApiRole
247+
lazy val canUpdateConsumerCertificate = CanUpdateConsumerCertificate()
246248
case class CanUpdateConsumerName(requiresBankId: Boolean = false) extends ApiRole
247249
lazy val canUpdateConsumerName = CanUpdateConsumerName()
248250

obp-api/src/main/scala/code/api/util/BerlinGroupCheck.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ import net.liftweb.http.provider.HTTPParam
99

1010
object BerlinGroupCheck {
1111

12+
13+
private val defaultMandatoryHeaders = "Content-Type,Date,Digest,PSU-Device-ID,PSU-Device-Name,PSU-IP-Address,Signature,TPP-Signature-Certificate,X-Request-ID"
1214
// Parse mandatory headers from a comma-separated string
13-
private val berlinGroupMandatoryHeaders: List[String] = APIUtil.getPropsValue("berlin_group_mandatory_headers", defaultValue = "X-Request-ID,PSU-IP-Address,PSU-Device-ID,PSU-Device-Name")
15+
private val berlinGroupMandatoryHeaders: List[String] = APIUtil.getPropsValue("berlin_group_mandatory_headers", defaultValue = defaultMandatoryHeaders)
1416
.split(",")
1517
.map(_.trim.toLowerCase)
1618
.toList.filterNot(_.isEmpty)

obp-api/src/main/scala/code/api/util/BerlinGroupError.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ object BerlinGroupError {
5656
case "401" if message.contains("OBP-20207") => "PSU_CREDENTIALS_INVALID"
5757

5858
case "401" if message.contains("OBP-20204") => "TOKEN_EXPIRED"
59+
case "401" if message.contains("OBP-20215") => "TOKEN_INVALID"
60+
case "401" if message.contains("OBP-20205") => "TOKEN_INVALID"
61+
case "401" if message.contains("OBP-20204") => "TOKEN_INVALID"
5962

6063
case "401" if message.contains("OBP-35003") => "CONSENT_EXPIRED"
6164

@@ -66,6 +69,11 @@ object BerlinGroupError {
6669
case "401" if message.contains("OBP-35018") => "CONSENT_INVALID"
6770
case "401" if message.contains("OBP-35005") => "CONSENT_INVALID"
6871

72+
case "403" if message.contains("OBP-35001") => "CONSENT_UNKNOWN"
73+
74+
case "401" if message.contains("OBP-20312") => "CERTIFICATE_INVALID"
75+
case "401" if message.contains("OBP-20310") => "SIGNATURE_INVALID"
76+
6977
case "401" if message.contains("OBP-20060") => "ROLE_INVALID"
7078

7179
case "400" if message.contains("OBP-35018") => "CONSENT_UNKNOWN"
@@ -78,6 +86,8 @@ object BerlinGroupError {
7886
case "400" if message.contains("OBP-10001") => "FORMAT_ERROR"
7987
case "400" if message.contains("OBP-20062") => "FORMAT_ERROR"
8088
case "400" if message.contains("OBP-20063") => "FORMAT_ERROR"
89+
case "400" if message.contains("OBP-20252") => "FORMAT_ERROR"
90+
case "400" if message.contains("OBP-20251") => "FORMAT_ERROR"
8191

8292
case "429" if message.contains("OBP-10018") => "ACCESS_EXCEEDED"
8393
case _ => code

obp-api/src/main/scala/code/api/util/BerlinGroupSigning.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,8 @@ object BerlinGroupSigning extends MdcLoggable {
156156
}
157157

158158
def getHeaderValue(name: String, requestHeaders: List[HTTPParam]): String = {
159-
requestHeaders.find(_.name.toLowerCase() == name.toLowerCase()).map(_.values.mkString).getOrElse("None")
159+
requestHeaders.find(_.name.toLowerCase() == name.toLowerCase()).map(_.values.mkString)
160+
.getOrElse(SecureRandomUtil.csprng.nextLong().toString)
160161
}
161162
private def getPem(requestHeaders: List[HTTPParam]): String = {
162163
val certificate = getHeaderValue(RequestHeader.`TPP-Signature-Certificate`, requestHeaders)

0 commit comments

Comments
 (0)