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
12 changes: 12 additions & 0 deletions obp-api/src/main/scala/code/api/OBPRestHelper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,18 @@ case class APIFailureNewStyle(failMsg: String,
}
}

object ObpApiFailure {
def apply(failMsg: String, failCode: Int = 400, cc: Option[CallContext] = None) = {
fullBoxOrException(Empty ~> APIFailureNewStyle(failMsg, failCode, cc.map(_.toLight)))
}

// overload for plain CallContext
def apply(failMsg: String, failCode: Int, cc: CallContext) = {
fullBoxOrException(Empty ~> APIFailureNewStyle(failMsg, failCode, Some(cc.toLight)))
}
}


//if you change this, think about backwards compatibility! All existing
//versions of the API return this failure message, so if you change it, make sure
//that all stable versions retain the same behavior
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3947,6 +3947,24 @@ object SwaggerDefinitionsJSON {
Some(redisCallLimitJson)
)

lazy val callLimitsJson510Example: CallLimitsJson510 = CallLimitsJson510(
limits = List(
CallLimitJson510(
rate_limiting_id = "80e1e0b2-d8bf-4f85-a579-e69ef36e3305",
from_date = DateWithDayExampleObject,
to_date = DateWithDayExampleObject,
per_second_call_limit = "100",
per_minute_call_limit = "100",
per_hour_call_limit = "-1",
per_day_call_limit = "-1",
per_week_call_limit = "-1",
per_month_call_limit = "-1",
created_at = DateWithDayExampleObject,
updated_at = DateWithDayExampleObject
)
)
)

lazy val accountWebhookPostJson = AccountWebhookPostJson(
account_id =accountIdExample.value,
trigger_name = ApiTrigger.onBalanceChange.toString(),
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 @@ -4021,20 +4021,20 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
} yield {
tpps match {
case Nil =>
Failure(RegulatedEntityNotFoundByCertificate)
ObpApiFailure(RegulatedEntityNotFoundByCertificate, 401, cc)
case single :: Nil =>
logger.debug(s"Regulated entity by certificate: $single")
// Only one match, proceed to role check
if (single.services.contains(serviceProvider)) {
logger.debug(s"Regulated entity by certificate (single.services: ${single.services}, serviceProvider: $serviceProvider): ")
Full(true)
} else {
Failure(X509ActionIsNotAllowed)
ObpApiFailure(X509ActionIsNotAllowed, 403, cc)
}
case multiple =>
// Ambiguity detected: more than one TPP matches the certificate
val names = multiple.map(e => s"'${e.entityName}' (Code: ${e.entityCode})").mkString(", ")
Failure(s"$RegulatedEntityAmbiguityByCertificate: multiple TPPs found: $names")
ObpApiFailure(s"$RegulatedEntityAmbiguityByCertificate: multiple TPPs found: $names", 401, cc)
}
}
case value if value.toUpperCase == "CERTIFICATE" => Future {
Expand Down
7 changes: 5 additions & 2 deletions obp-api/src/main/scala/code/api/util/BerlinGroupError.scala
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,14 @@ object BerlinGroupError {
case "401" if message.contains("OBP-35005") => "CONSENT_INVALID"

case "401" if message.contains("OBP-20300") => "CERTIFICATE_BLOCKED"
case "401" if message.contains("OBP-34102") => "CERTIFICATE_BLOCKED"
case "401" if message.contains("OBP-34103") => "CERTIFICATE_BLOCKED"

case "401" if message.contains("OBP-20312") => "CERTIFICATE_INVALID"
case "401" if message.contains("OBP-20300") => "CERTIFICATE_INVALID"
case "401" if message.contains("OBP-20310") => "SIGNATURE_INVALID"

case "401" if message.contains("OBP-20060") => "ROLE_INVALID"
case "403" if message.contains("OBP-20307") => "ROLE_INVALID"
case "403" if message.contains("OBP-20060") => "ROLE_INVALID"

case "400" if message.contains("OBP-10034") => "PARAMETER_NOT_CONSISTENT"

Expand Down
106 changes: 52 additions & 54 deletions obp-api/src/main/scala/code/api/util/BerlinGroupSigning.scala
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
package code.api.util

import code.api.{APIFailureNewStyle, RequestHeader}
import code.api.util.APIUtil.{OBPReturnType, fullBoxOrException}
import code.api.util.APIUtil.OBPReturnType
import code.api.util.ErrorUtil.apiFailure
import code.api.util.newstyle.RegulatedEntityNewStyle.getRegulatedEntitiesNewStyle
import code.api.{ObpApiFailure, RequestHeader}
import code.consumer.Consumers
import code.model.Consumer
import code.util.Helper.MdcLoggable
import com.openbankproject.commons.ExecutionContext.Implicits.global
import com.openbankproject.commons.model.{RegulatedEntityAttributeSimple, RegulatedEntityTrait, User}
import net.liftweb.common.{Box, Empty, Failure, Full}
import com.openbankproject.commons.model.{RegulatedEntityTrait, User}
import net.liftweb.common.{Box, Failure, Full}
import net.liftweb.http.provider.HTTPParam
import net.liftweb.util.Helpers

Expand Down Expand Up @@ -277,66 +277,64 @@ object BerlinGroupSigning extends MdcLoggable {
val tppSignatureCert: String = APIUtil.getRequestHeader(RequestHeader.`TPP-Signature-Certificate`, requestHeaders)
if (tppSignatureCert.isEmpty) {
Future(forwardResult)
} else { // Dynamic consumer creation/update works in case that RequestHeader.`TPP-Signature-Certificate is present in the current call
} else { // Dynamic consumer creation/update works in case that RequestHeader.`TPP-Signature-Certificate` is present
val certificate = getCertificateFromTppSignatureCertificate(requestHeaders)
// Use the regular expression to find the value of EMAILADDRESS
val extractedEmail = emailPattern.findFirstMatchIn(certificate.getSubjectDN.getName) match {
case Some(m) => Some(m.group(1)) // Extract the value of EMAILADDRESS
case None => None
}
// Use the regular expression to find the value of Organisation
val extractOrganisation = organisationlPattern.findFirstMatchIn(certificate.getSubjectDN.getName) match {
case Some(m) => Some(m.group(1)) // Extract the value of Organisation
case None => None
}

val extractedEmail = emailPattern.findFirstMatchIn(certificate.getSubjectDN.getName).map(_.group(1))
val extractOrganisation = organisationlPattern.findFirstMatchIn(certificate.getSubjectDN.getName).map(_.group(1))

for {
entities <- getRegulatedEntityByCertificate(certificate, forwardResult._2) // Find Regulated Entity via certificate
entities <- getRegulatedEntityByCertificate(certificate, forwardResult._2)
} yield {
// Certificate can be changed but this value is permanent per Regulated entity
val idno = entities.map(_.entityCode).headOption.getOrElse("")

val entityName = entities.map(_.entityName).headOption

// Get or create consumer by the unique key (azp, iss)
val consumer: Box[Consumer] = Consumers.consumers.vend.getOrCreateConsumer(
consumerId = None,
key = Some(Helpers.randomString(40).toLowerCase),
secret = Some(Helpers.randomString(40).toLowerCase),
aud = None,
azp = Some(idno), // The pair (azp, iss) is a unique key in case of Client of an Identity Provider
iss = Some(RequestHeader.`TPP-Signature-Certificate`),
sub = None,
Some(true),
name = entityName,
appType = None,
description = Some(s"Certificate serial number:${certificate.getSerialNumber}"),
developerEmail = extractedEmail,
redirectURL = None,
createdByUserId = None,
certificate = None,
logoUrl = code.api.Constant.consumerDefaultLogoUrl
)

// Set or update certificate
consumer match {
case Full(consumer) =>
val certificateFromHeader = getHeaderValue(RequestHeader.`TPP-Signature-Certificate`, requestHeaders)
Consumers.consumers.vend.updateConsumer(
id = consumer.id.get,
entities match {
case Nil =>
(ObpApiFailure(ErrorMessages.RegulatedEntityNotFoundByCertificate, 401, forwardResult._2), forwardResult._2)

case single :: Nil =>
val idno = single.entityCode
val entityName = Option(single.entityName)

val consumer: Box[Consumer] = Consumers.consumers.vend.getOrCreateConsumer(
consumerId = None,
key = Some(Helpers.randomString(40).toLowerCase),
secret = Some(Helpers.randomString(40).toLowerCase),
aud = None,
azp = Some(idno),
iss = Some(RequestHeader.`TPP-Signature-Certificate`),
sub = None,
Some(true),
name = entityName,
certificate = Some(certificateFromHeader)
) match {
appType = None,
description = Some(s"Certificate serial number:${certificate.getSerialNumber}"),
developerEmail = extractedEmail,
redirectURL = None,
createdByUserId = None,
certificate = None,
logoUrl = code.api.Constant.consumerDefaultLogoUrl
)

consumer match {
case Full(consumer) =>
// Update call context with a created consumer
(forwardResult._1, forwardResult._2.map(_.copy(consumer = Full(consumer))))
val certificateFromHeader = getHeaderValue(RequestHeader.`TPP-Signature-Certificate`, requestHeaders)
Consumers.consumers.vend.updateConsumer(
id = consumer.id.get,
name = entityName,
certificate = Some(certificateFromHeader)
) match {
case Full(updatedConsumer) =>
(forwardResult._1, forwardResult._2.map(_.copy(consumer = Full(updatedConsumer))))
case error =>
logger.debug(error)
(Failure(s"${ErrorMessages.CreateConsumerError} Regulated entity: $idno"), forwardResult._2)
}
case error =>
logger.debug(error)
(Failure(s"${ErrorMessages.CreateConsumerError} Regulated entity: $idno"), forwardResult._2)
}
case error =>
logger.debug(error)
(Failure(s"${ErrorMessages.CreateConsumerError} Regulated entity: $idno"), forwardResult._2)

case multiple =>
val names = multiple.map(e => s"'${e.entityName}' (Code: ${e.entityCode})").mkString(", ")
(ObpApiFailure(s"${ErrorMessages.RegulatedEntityAmbiguityByCertificate}: multiple TPPs found: $names", 401, forwardResult._2), forwardResult._2)
}
}
}
Expand Down
14 changes: 5 additions & 9 deletions obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3304,7 +3304,7 @@ trait APIMethods510 {
|
|""".stripMargin,
EmptyBody,
callLimitJson,
callLimitsJson510Example,
List(
$UserNotLoggedIn,
InvalidJsonFormat,
Expand All @@ -3319,19 +3319,15 @@ trait APIMethods510 {


lazy val getCallsLimit: OBPEndpoint = {
case "management" :: "consumers" :: consumerId :: "consumer" :: "call-limits" :: Nil JsonGet _ => {
case "management" :: "consumers" :: consumerId :: "consumer" :: "call-limits" :: Nil JsonGet _ =>
cc =>
implicit val ec = EndpointContext(Some(cc))
for {
// (Full(u), callContext) <- authenticatedAccess(cc)
// _ <- NewStyle.function.hasEntitlement("", cc.userId, canReadCallLimits, callContext)
consumer <- NewStyle.function.getConsumerByConsumerId(consumerId, cc.callContext)
rateLimiting: Option[RateLimiting] <- RateLimitingDI.rateLimiting.vend.findMostRecentRateLimit(consumerId, None, None, None)
rateLimit <- Future(RateLimitingUtil.consumerRateLimitState(consumer.consumerId.get).toList)
_ <- NewStyle.function.getConsumerByConsumerId(consumerId, cc.callContext)
rateLimiting <- RateLimitingDI.rateLimiting.vend.getAllByConsumerId(consumerId, None)
} yield {
(createCallLimitJson(consumer, rateLimiting, rateLimit), HttpCode.`200`(cc.callContext))
(createCallLimitJson(rateLimiting), HttpCode.`200`(cc.callContext))
}
}
}


Expand Down
61 changes: 20 additions & 41 deletions obp-api/src/main/scala/code/api/v5_1_0/JSONFactory5.1.0.scala
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,7 @@ case class ViewPermissionJson(
)

case class CallLimitJson510(
rate_limiting_id: String,
from_date: Date,
to_date: Date,
per_second_call_limit : String,
Expand All @@ -695,9 +696,9 @@ case class CallLimitJson510(
per_week_call_limit : String,
per_month_call_limit : String,
created_at : Date,
updated_at : Date,
current_state: Option[RedisCallLimitJson]
updated_at : Date
)
case class CallLimitsJson510(limits: List[CallLimitJson510])

object JSONFactory510 extends CustomJsonFormats with MdcLoggable {

Expand Down Expand Up @@ -1323,48 +1324,26 @@ object JSONFactory510 extends CustomJsonFormats with MdcLoggable {
)
}

def createCallLimitJson(consumer: Consumer, rateLimiting: Option[RateLimiting], rateLimits: List[((Option[Long], Option[Long]), LimitCallPeriod)]): CallLimitJson510 = {
val redisRateLimit = rateLimits match {
case Nil => None
case _ =>
def getInfo(period: RateLimitingPeriod.Value): Option[RateLimit] = {
rateLimits.filter(_._2 == period) match {
case x :: Nil =>
x._1 match {
case (Some(x), Some(y)) => Some(RateLimit(Some(x), Some(y)))
case _ => None

}
case _ => None
}
}

Some(
RedisCallLimitJson(
getInfo(RateLimitingPeriod.PER_SECOND),
getInfo(RateLimitingPeriod.PER_MINUTE),
getInfo(RateLimitingPeriod.PER_HOUR),
getInfo(RateLimitingPeriod.PER_DAY),
getInfo(RateLimitingPeriod.PER_WEEK),
getInfo(RateLimitingPeriod.PER_MONTH)
)
def createCallLimitJson(rateLimitings: List[RateLimiting]): CallLimitsJson510 = {
CallLimitsJson510(
rateLimitings.map( i =>
CallLimitJson510(
rate_limiting_id = i.rateLimitingId,
from_date = i.fromDate,
to_date = i.toDate,
per_second_call_limit = i.perSecondCallLimit.toString,
per_minute_call_limit = i.perMinuteCallLimit.toString,
per_hour_call_limit = i.perHourCallLimit.toString,
per_day_call_limit = i.perDayCallLimit.toString,
per_week_call_limit = i.perWeekCallLimit.toString,
per_month_call_limit = i.perMonthCallLimit.toString,
created_at = i.createdAt.get,
updated_at = i.updatedAt.get,
)
}

CallLimitJson510(
from_date = rateLimiting.map(_.fromDate).orNull,
to_date = rateLimiting.map(_.toDate).orNull,
per_second_call_limit = rateLimiting.map(_.perSecondCallLimit.toString).getOrElse("-1"),
per_minute_call_limit = rateLimiting.map(_.perMinuteCallLimit.toString).getOrElse("-1"),
per_hour_call_limit = rateLimiting.map(_.perHourCallLimit.toString).getOrElse("-1"),
per_day_call_limit = rateLimiting.map(_.perDayCallLimit.toString).getOrElse("-1"),
per_week_call_limit = rateLimiting.map(_.perWeekCallLimit.toString).getOrElse("-1"),
per_month_call_limit = rateLimiting.map(_.perMonthCallLimit.toString).getOrElse("-1"),
created_at = rateLimiting.map(_.createdAt.get).orNull,
updated_at = rateLimiting.map(_.updatedAt.get).orNull,
redisRateLimit
)
)


}

}
Loading