Skip to content

Commit 4a5e652

Browse files
authored
Merge pull request #2619 from constantine2nd/develop
Duplicate consumer creation on consent creation
2 parents 283925b + 87fc031 commit 4a5e652

19 files changed

+836
-120
lines changed

obp-api/src/main/scala/code/api/OBPRestHelper.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,18 @@ case class APIFailureNewStyle(failMsg: String,
159159
}
160160
}
161161

162+
object ObpApiFailure {
163+
def apply(failMsg: String, failCode: Int = 400, cc: Option[CallContext] = None) = {
164+
fullBoxOrException(Empty ~> APIFailureNewStyle(failMsg, failCode, cc.map(_.toLight)))
165+
}
166+
167+
// overload for plain CallContext
168+
def apply(failMsg: String, failCode: Int, cc: CallContext) = {
169+
fullBoxOrException(Empty ~> APIFailureNewStyle(failMsg, failCode, Some(cc.toLight)))
170+
}
171+
}
172+
173+
162174
//if you change this, think about backwards compatibility! All existing
163175
//versions of the API return this failure message, so if you change it, make sure
164176
//that all stable versions retain the same behavior

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3947,6 +3947,24 @@ object SwaggerDefinitionsJSON {
39473947
Some(redisCallLimitJson)
39483948
)
39493949

3950+
lazy val callLimitsJson510Example: CallLimitsJson510 = CallLimitsJson510(
3951+
limits = List(
3952+
CallLimitJson510(
3953+
rate_limiting_id = "80e1e0b2-d8bf-4f85-a579-e69ef36e3305",
3954+
from_date = DateWithDayExampleObject,
3955+
to_date = DateWithDayExampleObject,
3956+
per_second_call_limit = "100",
3957+
per_minute_call_limit = "100",
3958+
per_hour_call_limit = "-1",
3959+
per_day_call_limit = "-1",
3960+
per_week_call_limit = "-1",
3961+
per_month_call_limit = "-1",
3962+
created_at = DateWithDayExampleObject,
3963+
updated_at = DateWithDayExampleObject
3964+
)
3965+
)
3966+
)
3967+
39503968
lazy val accountWebhookPostJson = AccountWebhookPostJson(
39513969
account_id =accountIdExample.value,
39523970
trigger_name = ApiTrigger.onBalanceChange.toString(),

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4021,20 +4021,20 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
40214021
} yield {
40224022
tpps match {
40234023
case Nil =>
4024-
Failure(RegulatedEntityNotFoundByCertificate)
4024+
ObpApiFailure(RegulatedEntityNotFoundByCertificate, 401, cc)
40254025
case single :: Nil =>
40264026
logger.debug(s"Regulated entity by certificate: $single")
40274027
// Only one match, proceed to role check
40284028
if (single.services.contains(serviceProvider)) {
40294029
logger.debug(s"Regulated entity by certificate (single.services: ${single.services}, serviceProvider: $serviceProvider): ")
40304030
Full(true)
40314031
} else {
4032-
Failure(X509ActionIsNotAllowed)
4032+
ObpApiFailure(X509ActionIsNotAllowed, 403, cc)
40334033
}
40344034
case multiple =>
40354035
// Ambiguity detected: more than one TPP matches the certificate
40364036
val names = multiple.map(e => s"'${e.entityName}' (Code: ${e.entityCode})").mkString(", ")
4037-
Failure(s"$RegulatedEntityAmbiguityByCertificate: multiple TPPs found: $names")
4037+
ObpApiFailure(s"$RegulatedEntityAmbiguityByCertificate: multiple TPPs found: $names", 401, cc)
40384038
}
40394039
}
40404040
case value if value.toUpperCase == "CERTIFICATE" => Future {

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,14 @@ object BerlinGroupError {
7373
case "401" if message.contains("OBP-35005") => "CONSENT_INVALID"
7474

7575
case "401" if message.contains("OBP-20300") => "CERTIFICATE_BLOCKED"
76+
case "401" if message.contains("OBP-34102") => "CERTIFICATE_BLOCKED"
77+
case "401" if message.contains("OBP-34103") => "CERTIFICATE_BLOCKED"
78+
7679
case "401" if message.contains("OBP-20312") => "CERTIFICATE_INVALID"
77-
case "401" if message.contains("OBP-20300") => "CERTIFICATE_INVALID"
7880
case "401" if message.contains("OBP-20310") => "SIGNATURE_INVALID"
7981

80-
case "401" if message.contains("OBP-20060") => "ROLE_INVALID"
82+
case "403" if message.contains("OBP-20307") => "ROLE_INVALID"
83+
case "403" if message.contains("OBP-20060") => "ROLE_INVALID"
8184

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

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

Lines changed: 52 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
package code.api.util
22

3-
import code.api.{APIFailureNewStyle, RequestHeader}
4-
import code.api.util.APIUtil.{OBPReturnType, fullBoxOrException}
3+
import code.api.util.APIUtil.OBPReturnType
54
import code.api.util.ErrorUtil.apiFailure
65
import code.api.util.newstyle.RegulatedEntityNewStyle.getRegulatedEntitiesNewStyle
6+
import code.api.{ObpApiFailure, RequestHeader}
77
import code.consumer.Consumers
88
import code.model.Consumer
99
import code.util.Helper.MdcLoggable
1010
import com.openbankproject.commons.ExecutionContext.Implicits.global
11-
import com.openbankproject.commons.model.{RegulatedEntityAttributeSimple, RegulatedEntityTrait, User}
12-
import net.liftweb.common.{Box, Empty, Failure, Full}
11+
import com.openbankproject.commons.model.{RegulatedEntityTrait, User}
12+
import net.liftweb.common.{Box, Failure, Full}
1313
import net.liftweb.http.provider.HTTPParam
1414
import net.liftweb.util.Helpers
1515

@@ -277,66 +277,64 @@ object BerlinGroupSigning extends MdcLoggable {
277277
val tppSignatureCert: String = APIUtil.getRequestHeader(RequestHeader.`TPP-Signature-Certificate`, requestHeaders)
278278
if (tppSignatureCert.isEmpty) {
279279
Future(forwardResult)
280-
} else { // Dynamic consumer creation/update works in case that RequestHeader.`TPP-Signature-Certificate is present in the current call
280+
} else { // Dynamic consumer creation/update works in case that RequestHeader.`TPP-Signature-Certificate` is present
281281
val certificate = getCertificateFromTppSignatureCertificate(requestHeaders)
282-
// Use the regular expression to find the value of EMAILADDRESS
283-
val extractedEmail = emailPattern.findFirstMatchIn(certificate.getSubjectDN.getName) match {
284-
case Some(m) => Some(m.group(1)) // Extract the value of EMAILADDRESS
285-
case None => None
286-
}
287-
// Use the regular expression to find the value of Organisation
288-
val extractOrganisation = organisationlPattern.findFirstMatchIn(certificate.getSubjectDN.getName) match {
289-
case Some(m) => Some(m.group(1)) // Extract the value of Organisation
290-
case None => None
291-
}
282+
283+
val extractedEmail = emailPattern.findFirstMatchIn(certificate.getSubjectDN.getName).map(_.group(1))
284+
val extractOrganisation = organisationlPattern.findFirstMatchIn(certificate.getSubjectDN.getName).map(_.group(1))
292285

293286
for {
294-
entities <- getRegulatedEntityByCertificate(certificate, forwardResult._2) // Find Regulated Entity via certificate
287+
entities <- getRegulatedEntityByCertificate(certificate, forwardResult._2)
295288
} yield {
296-
// Certificate can be changed but this value is permanent per Regulated entity
297-
val idno = entities.map(_.entityCode).headOption.getOrElse("")
298-
299-
val entityName = entities.map(_.entityName).headOption
300-
301-
// Get or create consumer by the unique key (azp, iss)
302-
val consumer: Box[Consumer] = Consumers.consumers.vend.getOrCreateConsumer(
303-
consumerId = None,
304-
key = Some(Helpers.randomString(40).toLowerCase),
305-
secret = Some(Helpers.randomString(40).toLowerCase),
306-
aud = None,
307-
azp = Some(idno), // The pair (azp, iss) is a unique key in case of Client of an Identity Provider
308-
iss = Some(RequestHeader.`TPP-Signature-Certificate`),
309-
sub = None,
310-
Some(true),
311-
name = entityName,
312-
appType = None,
313-
description = Some(s"Certificate serial number:${certificate.getSerialNumber}"),
314-
developerEmail = extractedEmail,
315-
redirectURL = None,
316-
createdByUserId = None,
317-
certificate = None,
318-
logoUrl = code.api.Constant.consumerDefaultLogoUrl
319-
)
320-
321-
// Set or update certificate
322-
consumer match {
323-
case Full(consumer) =>
324-
val certificateFromHeader = getHeaderValue(RequestHeader.`TPP-Signature-Certificate`, requestHeaders)
325-
Consumers.consumers.vend.updateConsumer(
326-
id = consumer.id.get,
289+
entities match {
290+
case Nil =>
291+
(ObpApiFailure(ErrorMessages.RegulatedEntityNotFoundByCertificate, 401, forwardResult._2), forwardResult._2)
292+
293+
case single :: Nil =>
294+
val idno = single.entityCode
295+
val entityName = Option(single.entityName)
296+
297+
val consumer: Box[Consumer] = Consumers.consumers.vend.getOrCreateConsumer(
298+
consumerId = None,
299+
key = Some(Helpers.randomString(40).toLowerCase),
300+
secret = Some(Helpers.randomString(40).toLowerCase),
301+
aud = None,
302+
azp = Some(idno),
303+
iss = Some(RequestHeader.`TPP-Signature-Certificate`),
304+
sub = None,
305+
Some(true),
327306
name = entityName,
328-
certificate = Some(certificateFromHeader)
329-
) match {
307+
appType = None,
308+
description = Some(s"Certificate serial number:${certificate.getSerialNumber}"),
309+
developerEmail = extractedEmail,
310+
redirectURL = None,
311+
createdByUserId = None,
312+
certificate = None,
313+
logoUrl = code.api.Constant.consumerDefaultLogoUrl
314+
)
315+
316+
consumer match {
330317
case Full(consumer) =>
331-
// Update call context with a created consumer
332-
(forwardResult._1, forwardResult._2.map(_.copy(consumer = Full(consumer))))
318+
val certificateFromHeader = getHeaderValue(RequestHeader.`TPP-Signature-Certificate`, requestHeaders)
319+
Consumers.consumers.vend.updateConsumer(
320+
id = consumer.id.get,
321+
name = entityName,
322+
certificate = Some(certificateFromHeader)
323+
) match {
324+
case Full(updatedConsumer) =>
325+
(forwardResult._1, forwardResult._2.map(_.copy(consumer = Full(updatedConsumer))))
326+
case error =>
327+
logger.debug(error)
328+
(Failure(s"${ErrorMessages.CreateConsumerError} Regulated entity: $idno"), forwardResult._2)
329+
}
333330
case error =>
334331
logger.debug(error)
335332
(Failure(s"${ErrorMessages.CreateConsumerError} Regulated entity: $idno"), forwardResult._2)
336333
}
337-
case error =>
338-
logger.debug(error)
339-
(Failure(s"${ErrorMessages.CreateConsumerError} Regulated entity: $idno"), forwardResult._2)
334+
335+
case multiple =>
336+
val names = multiple.map(e => s"'${e.entityName}' (Code: ${e.entityCode})").mkString(", ")
337+
(ObpApiFailure(s"${ErrorMessages.RegulatedEntityAmbiguityByCertificate}: multiple TPPs found: $names", 401, forwardResult._2), forwardResult._2)
340338
}
341339
}
342340
}

obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3304,7 +3304,7 @@ trait APIMethods510 {
33043304
|
33053305
|""".stripMargin,
33063306
EmptyBody,
3307-
callLimitJson,
3307+
callLimitsJson510Example,
33083308
List(
33093309
$UserNotLoggedIn,
33103310
InvalidJsonFormat,
@@ -3319,19 +3319,15 @@ trait APIMethods510 {
33193319

33203320

33213321
lazy val getCallsLimit: OBPEndpoint = {
3322-
case "management" :: "consumers" :: consumerId :: "consumer" :: "call-limits" :: Nil JsonGet _ => {
3322+
case "management" :: "consumers" :: consumerId :: "consumer" :: "call-limits" :: Nil JsonGet _ =>
33233323
cc =>
33243324
implicit val ec = EndpointContext(Some(cc))
33253325
for {
3326-
// (Full(u), callContext) <- authenticatedAccess(cc)
3327-
// _ <- NewStyle.function.hasEntitlement("", cc.userId, canReadCallLimits, callContext)
3328-
consumer <- NewStyle.function.getConsumerByConsumerId(consumerId, cc.callContext)
3329-
rateLimiting: Option[RateLimiting] <- RateLimitingDI.rateLimiting.vend.findMostRecentRateLimit(consumerId, None, None, None)
3330-
rateLimit <- Future(RateLimitingUtil.consumerRateLimitState(consumer.consumerId.get).toList)
3326+
_ <- NewStyle.function.getConsumerByConsumerId(consumerId, cc.callContext)
3327+
rateLimiting <- RateLimitingDI.rateLimiting.vend.getAllByConsumerId(consumerId, None)
33313328
} yield {
3332-
(createCallLimitJson(consumer, rateLimiting, rateLimit), HttpCode.`200`(cc.callContext))
3329+
(createCallLimitJson(rateLimiting), HttpCode.`200`(cc.callContext))
33333330
}
3334-
}
33353331
}
33363332

33373333

obp-api/src/main/scala/code/api/v5_1_0/JSONFactory5.1.0.scala

Lines changed: 20 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,7 @@ case class ViewPermissionJson(
686686
)
687687

688688
case class CallLimitJson510(
689+
rate_limiting_id: String,
689690
from_date: Date,
690691
to_date: Date,
691692
per_second_call_limit : String,
@@ -695,9 +696,9 @@ case class CallLimitJson510(
695696
per_week_call_limit : String,
696697
per_month_call_limit : String,
697698
created_at : Date,
698-
updated_at : Date,
699-
current_state: Option[RedisCallLimitJson]
699+
updated_at : Date
700700
)
701+
case class CallLimitsJson510(limits: List[CallLimitJson510])
701702

702703
object JSONFactory510 extends CustomJsonFormats with MdcLoggable {
703704

@@ -1323,48 +1324,26 @@ object JSONFactory510 extends CustomJsonFormats with MdcLoggable {
13231324
)
13241325
}
13251326

1326-
def createCallLimitJson(consumer: Consumer, rateLimiting: Option[RateLimiting], rateLimits: List[((Option[Long], Option[Long]), LimitCallPeriod)]): CallLimitJson510 = {
1327-
val redisRateLimit = rateLimits match {
1328-
case Nil => None
1329-
case _ =>
1330-
def getInfo(period: RateLimitingPeriod.Value): Option[RateLimit] = {
1331-
rateLimits.filter(_._2 == period) match {
1332-
case x :: Nil =>
1333-
x._1 match {
1334-
case (Some(x), Some(y)) => Some(RateLimit(Some(x), Some(y)))
1335-
case _ => None
1336-
1337-
}
1338-
case _ => None
1339-
}
1340-
}
1341-
1342-
Some(
1343-
RedisCallLimitJson(
1344-
getInfo(RateLimitingPeriod.PER_SECOND),
1345-
getInfo(RateLimitingPeriod.PER_MINUTE),
1346-
getInfo(RateLimitingPeriod.PER_HOUR),
1347-
getInfo(RateLimitingPeriod.PER_DAY),
1348-
getInfo(RateLimitingPeriod.PER_WEEK),
1349-
getInfo(RateLimitingPeriod.PER_MONTH)
1350-
)
1327+
def createCallLimitJson(rateLimitings: List[RateLimiting]): CallLimitsJson510 = {
1328+
CallLimitsJson510(
1329+
rateLimitings.map( i =>
1330+
CallLimitJson510(
1331+
rate_limiting_id = i.rateLimitingId,
1332+
from_date = i.fromDate,
1333+
to_date = i.toDate,
1334+
per_second_call_limit = i.perSecondCallLimit.toString,
1335+
per_minute_call_limit = i.perMinuteCallLimit.toString,
1336+
per_hour_call_limit = i.perHourCallLimit.toString,
1337+
per_day_call_limit = i.perDayCallLimit.toString,
1338+
per_week_call_limit = i.perWeekCallLimit.toString,
1339+
per_month_call_limit = i.perMonthCallLimit.toString,
1340+
created_at = i.createdAt.get,
1341+
updated_at = i.updatedAt.get,
13511342
)
1352-
}
1353-
1354-
CallLimitJson510(
1355-
from_date = rateLimiting.map(_.fromDate).orNull,
1356-
to_date = rateLimiting.map(_.toDate).orNull,
1357-
per_second_call_limit = rateLimiting.map(_.perSecondCallLimit.toString).getOrElse("-1"),
1358-
per_minute_call_limit = rateLimiting.map(_.perMinuteCallLimit.toString).getOrElse("-1"),
1359-
per_hour_call_limit = rateLimiting.map(_.perHourCallLimit.toString).getOrElse("-1"),
1360-
per_day_call_limit = rateLimiting.map(_.perDayCallLimit.toString).getOrElse("-1"),
1361-
per_week_call_limit = rateLimiting.map(_.perWeekCallLimit.toString).getOrElse("-1"),
1362-
per_month_call_limit = rateLimiting.map(_.perMonthCallLimit.toString).getOrElse("-1"),
1363-
created_at = rateLimiting.map(_.createdAt.get).orNull,
1364-
updated_at = rateLimiting.map(_.updatedAt.get).orNull,
1365-
redisRateLimit
1343+
)
13661344
)
13671345

1346+
13681347
}
13691348

13701349
}

0 commit comments

Comments
 (0)