Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ recurringIndicator:
(Full(user), callContext) <- authenticatedAccess(cc)
_ <- passesPsd2Aisp(callContext)
_ <- Future(Consents.consentProvider.vend.getConsentByConsentId(consentId)) map {
unboxFullOrFail(_, callContext, ConsentNotFound)
unboxFullOrFail(_, callContext, ConsentNotFound, 403)
}
_ <- Future(Consents.consentProvider.vend.revokeBerlinGroupConsent(consentId)) map {
i => connectorEmptyResponse(i, callContext)
Expand Down Expand Up @@ -752,7 +752,7 @@ This method returns the SCA status of a consent initiation's authorisation sub-r
(_, callContext) <- authenticatedAccess(cc)
_ <- passesPsd2Aisp(callContext)
_ <- Future(Consents.consentProvider.vend.getConsentByConsentId(consentId)) map {
unboxFullOrFail(_, callContext, s"$ConsentNotFound ($consentId)")
unboxFullOrFail(_, callContext, s"$ConsentNotFound ($consentId)", 403)
}
(challenges, callContext) <- NewStyle.function.getChallengesByConsentId(consentId, callContext)
} yield {
Expand Down Expand Up @@ -787,7 +787,7 @@ This method returns the SCA status of a consent initiation's authorisation sub-r
(Full(u), callContext) <- authenticatedAccess(cc)
_ <- passesPsd2Aisp(callContext)
consent <- Future(Consents.consentProvider.vend.getConsentByConsentId(consentId)) map {
unboxFullOrFail(_, callContext, ConsentNotFound)
unboxFullOrFail(_, callContext, ConsentNotFound, 403)
}
} yield {
val status = consent.status
Expand Down Expand Up @@ -1134,7 +1134,7 @@ using the extended forms as indicated above.
(Full(u), callContext) <- authenticatedAccess(cc)
_ <- passesPsd2Aisp(callContext)
consent <- Future(Consents.consentProvider.vend.getConsentByConsentId(consentId)) map {
unboxFullOrFail(_, callContext, ConsentNotFound)
unboxFullOrFail(_, callContext, ConsentNotFound, 403)
}
(challenges, callContext) <- NewStyle.function.createChallengesC2(
List(u.userId),
Expand Down Expand Up @@ -1297,7 +1297,7 @@ Maybe in a later version the access path will change.
(Full(u), callContext) <- authenticatedAccess(cc)
_ <- passesPsd2Aisp(callContext)
_ <- Future(Consents.consentProvider.vend.getConsentByConsentId(consentId)) map {
unboxFullOrFail(_, callContext, ConsentNotFound)
unboxFullOrFail(_, callContext, ConsentNotFound, 403)
}
failMsg = s"$InvalidJsonFormat The Json body should be the $TransactionAuthorisation "
updateJson <- NewStyle.function.tryons(failMsg, 400, callContext) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats with MdcLoggable{
case class CoreAccountJsonV13(
resourceId: String,
iban: String,
bban: String,
bban: Option[String],
currency: String,
name: String,
product: String,
Expand Down Expand Up @@ -137,6 +137,10 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats with MdcLoggable{
iban: String,
currency : Option[String] = None,
)
case class FromAccountJson(
iban: String,
currency : Option[String] = None,
)
case class TransactionJsonV13(
transactionId: String,
creditorName: String,
Expand Down Expand Up @@ -190,7 +194,7 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats with MdcLoggable{
)

case class TransactionsJsonV13(
account:FromAccount,
account: FromAccountJson,
transactions:TransactionsV13Transactions,
)

Expand Down Expand Up @@ -259,7 +263,7 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats with MdcLoggable{
recurringIndicator: Boolean,
validUntil: String,
frequencyPerDay: Int,
combinedServiceIndicator: Boolean,
combinedServiceIndicator: Option[Boolean],
lastActionDate: String,
consentStatus: String
)
Expand Down Expand Up @@ -332,12 +336,12 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats with MdcLoggable{
CoreAccountJsonV13(
resourceId = x.accountId.value,
iban = iBan,
bban = bBan,
bban = None,
currency = x.currency,
name = x.name,
cashAccountType = x.accountType,
product = x.accountType,
balances = accountBalances,
balances = if(canReadBalances) accountBalances else None,
_links = CoreAccountLinksJsonV13(
balances = if(canReadBalances) Some(balanceRef) else None,
transactions = if(canReadTransactions) Some(transactionRef) else None,
Expand All @@ -363,7 +367,7 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats with MdcLoggable{
CoreAccountJsonV13(
resourceId = x.accountId.value,
iban = iBan,
bban = bBan,
bban = None,
currency = x.currency,
name = x.name,
cashAccountType = x.accountType,
Expand Down Expand Up @@ -440,14 +444,17 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats with MdcLoggable{
))
}

def createTransactionJSON(bankAccount: BankAccount, transaction : ModeratedTransaction, creditorAccount: CreditorAccountJson) : TransactionJsonV13 = {
val bookingDate = transaction.startDate.getOrElse(null)
val valueDate = transaction.finishDate.getOrElse(null)
def createTransactionJSON(bankAccount: BankAccount, transaction : ModeratedTransaction) : TransactionJsonV13 = {
val bookingDate = transaction.startDate.orNull
val valueDate = transaction.finishDate.orNull
val creditorName = bankAccount.label
TransactionJsonV13(
transactionId = transaction.id.value,
creditorName = creditorName,
creditorAccount = creditorAccount,
creditorAccount = CreditorAccountJson(
transaction.otherBankAccount.map(_.iban.orNull).orNull,
transaction.currency
),
transactionAmount = AmountOfMoneyV13(APIUtil.stringOptionOrNull(transaction.currency), transaction.amount.get.toString().trim.stripPrefix("-")),
bookingDate = BgSpecValidation.formatToISODate(bookingDate) ,
valueDate = BgSpecValidation.formatToISODate(valueDate),
Expand Down Expand Up @@ -476,16 +483,19 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats with MdcLoggable{
}


def createTransactionFromRequestJSON(bankAccount: BankAccount, transactionRequest : TransactionRequest, creditorAccount: CreditorAccountJson) : TransactionJsonV13 = {
def createTransactionFromRequestJSON(bankAccount: BankAccount, tr : TransactionRequest) : TransactionJsonV13 = {
val creditorName = bankAccount.accountHolder
val remittanceInformationUnstructured = stringOrNull(transactionRequest.body.description)
val remittanceInformationUnstructured = stringOrNull(tr.body.description)
TransactionJsonV13(
transactionId = transactionRequest.id.value,
transactionId = tr.id.value,
creditorName = creditorName,
creditorAccount = creditorAccount,
transactionAmount = AmountOfMoneyV13(transactionRequest.charge.value.currency, transactionRequest.charge.value.amount.trim.stripPrefix("-")),
bookingDate = BgSpecValidation.formatToISODate(transactionRequest.start_date),
valueDate = BgSpecValidation.formatToISODate(transactionRequest.end_date),
creditorAccount = CreditorAccountJson(
if (tr.other_account_routing_scheme == "IBAN") tr.other_account_routing_address else "",
Some(tr.body.value.currency)
),
transactionAmount = AmountOfMoneyV13(tr.charge.value.currency, tr.charge.value.amount.trim.stripPrefix("-")),
bookingDate = BgSpecValidation.formatToISODate(tr.start_date),
valueDate = BgSpecValidation.formatToISODate(tr.end_date),
remittanceInformationUnstructured = remittanceInformationUnstructured
)
}
Expand All @@ -494,17 +504,15 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats with MdcLoggable{
val accountId = bankAccount.accountId.value
val (iban: String, bban: String) = getIbanAndBban(bankAccount)

val creditorAccount = CreditorAccountJson(
val account = FromAccountJson(
iban = iban,
currency = Some(bankAccount.currency)
)
TransactionsJsonV13(
FromAccount(
iban = iban,
),
account,
TransactionsV13Transactions(
booked= transactions.map(transaction => createTransactionJSON(bankAccount, transaction, creditorAccount)),
pending = transactionRequests.filter(_.status!="COMPLETED").map(transactionRequest => createTransactionFromRequestJSON(bankAccount, transactionRequest, creditorAccount)),
booked= transactions.map(transaction => createTransactionJSON(bankAccount, transaction)),
pending = transactionRequests.filter(_.status!="COMPLETED").map(transactionRequest => createTransactionFromRequestJSON(bankAccount, transactionRequest)),
_links = TransactionsV13TransactionsLinks(LinkHrefJson(s"/v1.3/accounts/$accountId"))
)
)
Expand Down Expand Up @@ -626,7 +634,7 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats with MdcLoggable{
recurringIndicator = createdConsent.recurringIndicator,
validUntil = if(createdConsent.validUntil == null) null else new SimpleDateFormat(DateWithDay).format(createdConsent.validUntil),
frequencyPerDay = createdConsent.frequencyPerDay,
combinedServiceIndicator= createdConsent.combinedServiceIndicator,
combinedServiceIndicator = None,
lastActionDate = if(createdConsent.lastActionDate == null) null else new SimpleDateFormat(DateWithDay).format(createdConsent.lastActionDate),
consentStatus = createdConsent.status.toLowerCase()
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ This method returns the SCA status of a signing basket's authorisation sub-resou
(Full(u), callContext) <- authenticatedAccess(cc)
_ <- passesPsd2Pisp(callContext)
_ <- Future(SigningBasketX.signingBasketProvider.vend.getSigningBasketByBasketId(basketId)) map {
unboxFullOrFail(_, callContext, s"$ConsentNotFound ($basketId)")
unboxFullOrFail(_, callContext, s"$ConsentNotFound ($basketId)", 403)
}
(challenges, callContext) <- NewStyle.function.getChallengesByBasketId(basketId, callContext)
} yield {
Expand Down
2 changes: 2 additions & 0 deletions obp-api/src/main/scala/code/api/constant/constant.scala
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ object RequestHeader {
final lazy val `PSD2-CERT` = "PSD2-CERT"
final lazy val `If-None-Match` = "If-None-Match"

final lazy val `PSU-Device-ID` = "PSU-Device-ID" // Berlin Group
final lazy val `PSU-IP-Address` = "PSU-IP-Address" // Berlin Group
final lazy val `X-Request-ID` = "X-Request-ID" // Berlin Group
final lazy val `TPP-Redirect-URI` = "TPP-Redirect-URI" // Berlin Group
final lazy val `TPP-Nok-Redirect-URI` = "TPP-Nok-Redirect-URI" // Redirect URI in case of an error.
Expand Down
6 changes: 3 additions & 3 deletions obp-api/src/main/scala/code/api/util/APIUtil.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3001,12 +3001,12 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
val res =
if (authHeadersWithEmptyValues.nonEmpty) { // Check Authorization Headers Empty Values
val message = ErrorMessages.EmptyRequestHeaders + s"Header names: ${authHeadersWithEmptyValues.mkString(", ")}"
Future { (fullBoxOrException(Empty ~> APIFailureNewStyle(message, 400, Some(cc.toLight))), None) }
Future { (fullBoxOrException(Empty ~> APIFailureNewStyle(message, 400, Some(cc.toLight))), Some(cc)) }
} else if (authHeadersWithEmptyNames.nonEmpty) { // Check Authorization Headers Empty Names
val message = ErrorMessages.EmptyRequestHeaders + s"Header values: ${authHeadersWithEmptyNames.mkString(", ")}"
Future { (fullBoxOrException(Empty ~> APIFailureNewStyle(message, 400, Some(cc.toLight))), None) }
Future { (fullBoxOrException(Empty ~> APIFailureNewStyle(message, 400, Some(cc.toLight))), Some(cc)) }
} else if (authHeaders.size > 1) { // Check Authorization Headers ambiguity
Future { (Failure(ErrorMessages.AuthorizationHeaderAmbiguity + s"${authHeaders}"), None) }
Future { (Failure(ErrorMessages.AuthorizationHeaderAmbiguity + s"${authHeaders}"), Some(cc)) }
} else if (APIUtil.`hasConsent-ID`(reqHeaders)) { // Berlin Group's Consent
Consent.applyBerlinGroupRules(APIUtil.`getConsent-ID`(reqHeaders), cc.copy(consumer = consumerByCertificate))
} else if (APIUtil.hasConsentJWT(reqHeaders)) { // Open Bank Project's Consent
Expand Down
3 changes: 1 addition & 2 deletions obp-api/src/main/scala/code/api/util/BerlinGroupError.scala
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,6 @@ object BerlinGroupError {
case "401" if message.contains("OBP-35018") => "CONSENT_INVALID"
case "401" if message.contains("OBP-35005") => "CONSENT_INVALID"

case "403" if message.contains("OBP-35001") => "CONSENT_UNKNOWN"

case "401" if message.contains("OBP-20300") => "CERTIFICATE_BLOCKED"
case "401" if message.contains("OBP-20312") => "CERTIFICATE_INVALID"
case "401" if message.contains("OBP-20300") => "CERTIFICATE_INVALID"
Expand All @@ -80,6 +78,7 @@ object BerlinGroupError {

case "400" if message.contains("OBP-35018") => "CONSENT_UNKNOWN"
case "400" if message.contains("OBP-35001") => "CONSENT_UNKNOWN"
case "403" if message.contains("OBP-35001") => "CONSENT_UNKNOWN"

case "404" if message.contains("OBP-30076") => "RESOURCE_UNKNOWN"

Expand Down
9 changes: 6 additions & 3 deletions obp-api/src/main/scala/code/api/util/BerlinGroupSigning.scala
Original file line number Diff line number Diff line change
Expand Up @@ -160,14 +160,15 @@ object BerlinGroupSigning extends MdcLoggable {
val certificate = getCertificateFromTppSignatureCertificate(requestHeaders)
X509.validateCertificate(certificate) match {
case Full(true) => // PEM certificate is ok
val digest = generateDigest(body.getOrElse(""))
if(digest == getHeaderValue(RequestHeader.Digest, requestHeaders)) { // Verifying the Hash in the Digest Field
val generatedDigest = generateDigest(body.getOrElse(""))
val requestHeaderDigest = getHeaderValue(RequestHeader.Digest, requestHeaders)
if(generatedDigest == requestHeaderDigest) { // Verifying the Hash in the Digest Field
val signatureHeaderValue = getHeaderValue(RequestHeader.Signature, requestHeaders)
val signature = parseSignatureHeader(signatureHeaderValue).getOrElse("signature", "NONE")
val headersToSign = parseSignatureHeader(signatureHeaderValue).getOrElse("headers", "").split(" ").toList
val headers = headersToSign.map(h =>
if (h.toLowerCase() == RequestHeader.Digest.toLowerCase()) {
s"$h: $digest"
s"$h: $generatedDigest"
} else {
s"$h: ${getHeaderValue(h, requestHeaders)}"
}
Expand All @@ -183,6 +184,8 @@ object BerlinGroupSigning extends MdcLoggable {
case (false, _) => (Failure(ErrorMessages.X509PublicKeyCannotVerify), forwardResult._2)
}
} else { // The two DIGEST hashes do NOT match, the integrity of the request body is NOT confirmed.
logger.debug(s"Generated digest: $generatedDigest")
logger.debug(s"Request header digest: $requestHeaderDigest")
(Failure(ErrorMessages.X509PublicKeyCannotVerify), forwardResult._2)
}
case Failure(msg, t, c) => (Failure(msg, t, c), forwardResult._2) // PEM certificate is not valid
Expand Down
Loading