Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion obp-api/src/main/scala/code/api/dauth.scala
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ object DAuth extends MdcLoggable {

// Check if the request (access token or request token) is valid and return a tuple
def getDAuthToken(requestHeaders: List[HTTPParam]) : Option[List[String]] = {
requestHeaders.find(_.name==APIUtil.DAuthHeaderKey).map(_.values)
requestHeaders.find(_.name.equalsIgnoreCase(APIUtil.DAuthHeaderKey)).map(_.values)
}

def getOrCreateResourceUser(jwtPayload: String, callContext: Option[CallContext]) : Box[(User, Option[CallContext])] = {
Expand Down
38 changes: 19 additions & 19 deletions obp-api/src/main/scala/code/api/util/APIUtil.scala
Original file line number Diff line number Diff line change
Expand Up @@ -239,9 +239,9 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{

def hasDirectLoginHeader(authorization: Box[String]): Boolean = hasHeader("DirectLogin", authorization)

def has2021DirectLoginHeader(requestHeaders: List[HTTPParam]): Boolean = requestHeaders.find(_.name.toLowerCase == "DirectLogin".toLowerCase()).isDefined
def has2021DirectLoginHeader(requestHeaders: List[HTTPParam]): Boolean = requestHeaders.exists(_.name.equalsIgnoreCase("DirectLogin"))

def hasAuthorizationHeader(requestHeaders: List[HTTPParam]): Boolean = requestHeaders.find(_.name == "Authorization").isDefined
def hasAuthorizationHeader(requestHeaders: List[HTTPParam]): Boolean = requestHeaders.exists(_.name.equalsIgnoreCase("Authorization"))

/*
The OAuth 2.0 Authorization Framework: Bearer Token
Expand All @@ -262,7 +262,7 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
* Other types: the `GatewayLogin` is in the VALUE
* Authorization:GatewayLogin token=xxxx
*/
def hasDAuthHeader(requestHeaders: List[HTTPParam]) = requestHeaders.map(_.name).exists(_ ==DAuthHeaderKey)
def hasDAuthHeader(requestHeaders: List[HTTPParam]) = requestHeaders.exists(_.name.equalsIgnoreCase(DAuthHeaderKey))

/**
* Helper function which tells us does an "Authorization" request header field has the Type of an authentication scheme
Expand All @@ -282,9 +282,9 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
* @return the Consent-JWT value from a Request Header as a String
*/
def getConsentJWT(requestHeaders: List[HTTPParam]): Option[String] = {
requestHeaders.toSet.filter(_.name == RequestHeader.`Consent-JWT`).toList match {
requestHeaders.toSet.filter(_.name.equalsIgnoreCase(RequestHeader.`Consent-JWT`)).toList match {
case x :: Nil => Some(x.values.mkString(", "))
case _ => requestHeaders.toSet.filter(_.name == RequestHeader.`Consent-Id`).toList match {
case _ => requestHeaders.toSet.filter(_.name.equalsIgnoreCase(RequestHeader.`Consent-Id`)).toList match {
case x :: Nil => Some(x.values.mkString(", "))
case _ => None
}
Expand All @@ -296,7 +296,7 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
* @return the Consent-JWT value from a Request Header as a String
*/
def getConsentIdRequestHeaderValue(requestHeaders: List[HTTPParam]): Option[String] = {
requestHeaders.toSet.filter(_.name == RequestHeader.`Consent-Id`).toList match {
requestHeaders.toSet.filter(_.name.equalsIgnoreCase(RequestHeader.`Consent-Id`)).toList match {
case x :: Nil => Some(x.values.mkString(", "))
case _ => None
}
Expand All @@ -306,14 +306,14 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
* @return the PSD2-CERT value from a Request Header as a String
*/
def `getPSD2-CERT`(requestHeaders: List[HTTPParam]): Option[String] = {
requestHeaders.toSet.filter(_.name == RequestHeader.`PSD2-CERT`).toList match {
requestHeaders.toSet.filter(_.name.equalsIgnoreCase(RequestHeader.`PSD2-CERT`)).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 {
requestHeaders.toSet.filter(_.name.equalsIgnoreCase(name)).toList match {
case x :: Nil => x.values.mkString(";")
case _ => ""
}
Expand All @@ -329,7 +329,7 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
* @return the Consent-ID value from a Request Header as a String
*/
def `getConsent-ID`(requestHeaders: List[HTTPParam]): Option[String] = {
requestHeaders.toSet.filter(_.name == RequestHeader.`Consent-ID`).toList match {
requestHeaders.toSet.filter(_.name.equalsIgnoreCase(RequestHeader.`Consent-ID`)).toList match {
case x :: Nil => Some(x.values.mkString(", "))
case _ => None
}
Expand Down Expand Up @@ -499,13 +499,13 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{

private def checkConditionalRequest(cc: Option[CallContext], httpVerb: String, httpCode: Int, httpBody: Box[String]) = {
val requestHeaders: List[HTTPParam] = cc.map(_.requestHeaders).getOrElse(Nil)
requestHeaders.filter(_.name == RequestHeader.`If-None-Match` ).headOption match {
requestHeaders.filter(_.name.equalsIgnoreCase(RequestHeader.`If-None-Match`)).headOption match {
case Some(value) => // Handle the If-None-Match HTTP request header
checkIfNotMatchHeader(cc, httpCode, httpBody, value.values.mkString(""))
case None =>
// When used in combination with If-None-Match, it is ignored, unless the server doesn't support If-None-Match.
// The most common use case is to update a cached entity that has no associated ETag
requestHeaders.filter(_.name == RequestHeader.`If-Modified-Since` ).headOption match {
requestHeaders.filter(_.name.equalsIgnoreCase(RequestHeader.`If-Modified-Since`)).headOption match {
case Some(value) => // Handle the If-Modified-Since HTTP request header
checkIfModifiedSinceHeader(cc, httpVerb, httpCode, httpBody, value.values.mkString(""))
case None =>
Expand Down Expand Up @@ -2842,7 +2842,7 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
} else if (BerlinGroupCheck.hasUnwantedConsentIdHeaderForBGEndpoint(url, reqHeaders)) {
val message = ErrorMessages.InvalidConsentIdUsage
Future { (fullBoxOrException(Empty ~> APIFailureNewStyle(message, 400, Some(cc.toLight))), Some(cc)) }
} else if (APIUtil.`hasConsent-ID`(reqHeaders)) { // Berlin Group's Consent
} else if (url.contains(ConstantsBG.berlinGroupVersion1.urlPrefix) && APIUtil.`hasConsent-ID`(reqHeaders)) { // Berlin Group's Consent
// Choose consumer based on validation method configuration
val consumerForConsent = if (method == "CONSUMER_KEY_VALUE" && consumerByConsumerKey.isDefined) {
consumerByConsumerKey
Expand Down Expand Up @@ -4658,7 +4658,7 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
* @return Full(errorResponse) if validate fail
*/
def validateRequestHeadersKeys(operationId: String, callContext: CallContext): Box[JsonResponse] = {
val headerKeysGrouped: Map[String, List[HTTPParam]] = callContext.requestHeaders.groupBy(x => x.name)
val headerKeysGrouped: Map[String, List[HTTPParam]] = callContext.requestHeaders.groupBy(_.name.toLowerCase(java.util.Locale.ROOT))
headerKeysGrouped.toList.forall(_._2.size == 1) match {
case true => Empty
case false =>
Expand Down Expand Up @@ -4747,12 +4747,12 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
case (Some(callContext), operationId) if enableForceError =>
val requestHeaders = callContext.requestHeaders

val forceError = requestHeaders.collectFirst({
case HTTPParam("Force-Error", value::_) => value
})
val responseCode = requestHeaders.collectFirst({
case HTTPParam("Response-Code", value::_) => value
})
val forceError = requestHeaders.collectFirst {
case HTTPParam(name, value::_) if name.equalsIgnoreCase("Force-Error") => value
}
val responseCode = requestHeaders.collectFirst {
case HTTPParam(name, value::_) if name.equalsIgnoreCase("Response-Code") => value
}

if(forceError.isEmpty) {
Empty
Expand Down
7 changes: 3 additions & 4 deletions obp-api/src/main/scala/code/api/util/AuthorisationUtil.scala
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,9 @@ import code.api.util.APIUtil.HTTPParam

object AuthorisationUtil {
def getAuthorisationHeaders(requestHeaders: List[HTTPParam]): List[String] = {
requestHeaders.map(_.name).filter {
case `Consent-Id`| `Consent-ID` | `Consent-JWT` => true
case _ => false
}
requestHeaders.map(_.name).filter(name =>
List(`Consent-Id`, `Consent-ID`, `Consent-JWT`).exists(name.equalsIgnoreCase)
)
}


Expand Down
14 changes: 7 additions & 7 deletions obp-api/src/main/scala/code/api/util/ConsentUtil.scala
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ object Consent extends MdcLoggable {
* @return the Consumer-Key value from a Request Header as a String
*/
def getConsumerKey(requestHeaders: List[HTTPParam]): Option[String] = {
requestHeaders.toSet.filter(_.name == RequestHeader.`Consumer-Key`).toList match {
requestHeaders.toSet.filter(_.name.equalsIgnoreCase(RequestHeader.`Consumer-Key`)).toList match {
case x :: Nil => Some(x.values.mkString(", "))
case _ => None
}
Expand Down Expand Up @@ -1551,12 +1551,12 @@ object Consent extends MdcLoggable {

// Collect optional headers
val headers = callContext.map(_.requestHeaders).getOrElse(Nil)
val tppRedirectUri = headers.find(_.name == RequestHeader.`TPP-Redirect-URI`)
val tppNokRedirectUri = headers.find(_.name == RequestHeader.`TPP-Nok-Redirect-URI`)
val xRequestId = headers.find(_.name == RequestHeader.`X-Request-ID`)
val psuDeviceId = headers.find(_.name == RequestHeader.`PSU-Device-ID`)
val psuIpAddress = headers.find(_.name == RequestHeader.`PSU-IP-Address`)
val psuGeoLocation = headers.find(_.name == RequestHeader.`PSU-Geo-Location`)
val tppRedirectUri = headers.find(_.name.equalsIgnoreCase(RequestHeader.`TPP-Redirect-URI`))
val tppNokRedirectUri = headers.find(_.name.equalsIgnoreCase(RequestHeader.`TPP-Nok-Redirect-URI`))
val xRequestId = headers.find(_.name.equalsIgnoreCase(RequestHeader.`X-Request-ID`))
val psuDeviceId = headers.find(_.name.equalsIgnoreCase(RequestHeader.`PSU-Device-ID`))
val psuIpAddress = headers.find(_.name.equalsIgnoreCase(RequestHeader.`PSU-IP-Address`))
val psuGeoLocation = headers.find(_.name.equalsIgnoreCase(RequestHeader.`PSU-Geo-Location`))

def sequenceBoxes[A](boxes: List[Box[A]]): Box[List[A]] = {
boxes.foldRight(Full(Nil): Box[List[A]]) { (box, acc) =>
Expand Down
10 changes: 5 additions & 5 deletions obp-api/src/main/scala/code/api/util/JwsUtil.scala
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ object JwsUtil extends MdcLoggable {
json.parse(s).extractOpt[JwsProtectedHeader] match {
case Some(header) =>
val headers = header.sigD.pars.flatMap( i =>
requestHeaders.find(_.name.toLowerCase() == i.toLowerCase()).map(i => s"${i.name.toLowerCase()}: ${i.values.mkString}")
requestHeaders.find(_.name.equalsIgnoreCase(i)).map(i => s"${i.name.toLowerCase()}: ${i.values.mkString}")
)
val requestTarget = s"""(request-target): ${verb.toLowerCase()} ${url}\n"""
requestTarget + headers.mkString("\n") + "\n" // Add new line after each item
Expand All @@ -100,14 +100,14 @@ object JwsUtil extends MdcLoggable {
headerValue == s"SHA-256=${computeDigest(httpBody)}"
}
def getDigestHeaderValue(requestHeaders: List[HTTPParam]): String = {
requestHeaders.find(_.name.toLowerCase == "digest").map(_.values.mkString).getOrElse("None")
requestHeaders.find(_.name.equalsIgnoreCase("digest")).map(_.values.mkString).getOrElse("None")
}
def getJwsHeaderValue(requestHeaders: List[HTTPParam]): String = {
requestHeaders.find(_.name == "x-jws-signature").map(_.values.mkString).getOrElse("None")
requestHeaders.find(_.name.equalsIgnoreCase("x-jws-signature")).map(_.values.mkString).getOrElse("None")
}
def checkRequestIsSigned(requestHeaders: List[HTTPParam]): Boolean = {
requestHeaders.find(_.name == "x-jws-signature").isDefined ||
requestHeaders.find(_.name == "digest").isDefined
requestHeaders.exists(_.name.equalsIgnoreCase("x-jws-signature")) ||
requestHeaders.exists(_.name.equalsIgnoreCase("digest"))
}
private def getDeferredCriticalHeaders() = {
val deferredCriticalHeaders = new util.HashSet[String]()
Expand Down
2 changes: 2 additions & 0 deletions obp-api/src/main/scala/code/api/util/PegdownOptions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ object PegdownOptions {
.replaceAll("&;", "&")
.replaceAll("‘", "'")
.replaceAll("…", "...")
.replaceAll("–", "–")
.replaceAll("—", "—")
// not support make text bold that not at beginning of a line, so here manual convert to it to <strong> tag
// .replaceAll("""\*\*(.+?)\*\*""", "<strong>$1</strong>")
}
Expand Down
2 changes: 1 addition & 1 deletion obp-api/src/main/scala/code/api/util/WriteMetricUtil.scala
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ object WriteMetricUtil extends MdcLoggable {
}

private def requestHeaderValue(cc: CallContextLight, headerName: String): String =
cc.requestHeaders.find(_.name.toLowerCase() == headerName).map(_.values.mkString(",")).getOrElse("")
cc.requestHeaders.find(_.name.equalsIgnoreCase(headerName)).map(_.values.mkString(",")).getOrElse("")

private def saveMetricSafely(cc: CallContextLight, fields: MetricFields): Unit = {
import fields._
Expand Down
26 changes: 26 additions & 0 deletions obp-api/src/test/scala/code/util/APIUtilHeaderTest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package code.util

import code.api.DAuth
import code.api.util.{APIUtil, AuthorisationUtil, Consent, JwsUtil}
import code.api.v4_0_0.V400ServerSetup

class APIUtilHeaderTest extends V400ServerSetup {

feature("Consent and PSD2 request header lookup") {
scenario("HTTP/2 lowercase consent and PSD2 header names are accepted") {
APIUtil.getConsentJWT(List(APIUtil.HTTPParam("consent-jwt", List("jwt")))) shouldBe Some("jwt")

Check failure on line 11 in obp-api/src/test/scala/code/util/APIUtilHeaderTest.scala

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "consent-jwt" 3 times.

See more on https://sonarcloud.io/project/issues?id=OpenBankProject_OBP-API&issues=AaCvpdFC79sSKqMSTyhf&open=AaCvpdFC79sSKqMSTyhf&pullRequest=2915
APIUtil.getConsentJWT(List(APIUtil.HTTPParam("consent-id", List("consent-id")))) shouldBe Some("consent-id")

Check failure on line 12 in obp-api/src/test/scala/code/util/APIUtilHeaderTest.scala

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "consent-id" 7 times.

See more on https://sonarcloud.io/project/issues?id=OpenBankProject_OBP-API&issues=AaCvpdFC79sSKqMSTyhd&open=AaCvpdFC79sSKqMSTyhd&pullRequest=2915
APIUtil.getConsentIdRequestHeaderValue(List(APIUtil.HTTPParam("consent-id", List("consent-id")))) shouldBe Some("consent-id")
APIUtil.`getPSD2-CERT`(List(APIUtil.HTTPParam("psd2-cert", List("certificate")))) shouldBe Some("certificate")
APIUtil.`getConsent-ID`(List(APIUtil.HTTPParam("consent-id", List("berlin-group-consent-id")))) shouldBe Some("berlin-group-consent-id")
Consent.getConsumerKey(List(APIUtil.HTTPParam("consumer-key", List("consumer-key")))) shouldBe Some("consumer-key")

Check failure on line 16 in obp-api/src/test/scala/code/util/APIUtilHeaderTest.scala

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "consumer-key" 3 times.

See more on https://sonarcloud.io/project/issues?id=OpenBankProject_OBP-API&issues=AaCvpdFC79sSKqMSTyhe&open=AaCvpdFC79sSKqMSTyhe&pullRequest=2915
APIUtil.getRequestHeader("PSU-ID", List(APIUtil.HTTPParam("psu-id", List("psu")))) shouldBe "psu"
APIUtil.hasAuthorizationHeader(List(APIUtil.HTTPParam("authorization", List("Bearer token")))) shouldBe true
APIUtil.hasDAuthHeader(List(APIUtil.HTTPParam("dauth", List("token")))) shouldBe true
DAuth.getDAuthToken(List(APIUtil.HTTPParam("dauth", List("token")))) shouldBe Some(List("token"))
JwsUtil.getJwsHeaderValue(List(APIUtil.HTTPParam("X-JWS-SIGNATURE", List("signature")))) shouldBe "signature"
JwsUtil.checkRequestIsSigned(List(APIUtil.HTTPParam("DIGEST", List("digest")))) shouldBe true
AuthorisationUtil.getAuthorisationHeaders(List(APIUtil.HTTPParam("consent-jwt", List("jwt")))) shouldBe List("consent-jwt")
}
}
}
8 changes: 8 additions & 0 deletions obp-api/src/test/scala/code/util/PegdownOptionsTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,14 @@ class PegdownOptionsTest extends FlatSpec with Matchers {
val html = PegdownOptions.convertGitHubDocMarkdownToHtml(markdownText)
}

it should "render typographic dashes as XML-safe characters" taggedAs FunctionsTag in {
val html = convertPegdownToHtmlTweaked("A consent -- and a longer --- separator")

html should not include "&ndash;"
html should not include "&mdash;"
stringToNodeSeq(html)
}

"description string" should "test the markdown * -> html <li> tag" taggedAs FunctionsTag in {

// This string is from Foobar Property List: format
Expand Down
Loading