Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
d9a7736
docfix/Improve Create Bank docs
constantine2nd Oct 10, 2025
6707f0b
docfix/Improve Create Bank docs 2
constantine2nd Oct 14, 2025
456a0c0
feature/Apply Secure Logging Masking Configuration in case of CallCon…
constantine2nd Oct 16, 2025
42fb588
bugfix/Create Call Limits for a Consumer v6.0.0
constantine2nd Oct 29, 2025
be38699
Merge remote-tracking branch 'upstream/develop' into develop
constantine2nd Oct 29, 2025
b31b6ff
feature/Rate Limiting endpoint tweaks
constantine2nd Oct 29, 2025
4ca97c7
feature/Rate Limiting endpoint tweaks 2
constantine2nd Oct 30, 2025
74592c6
Merge remote-tracking branch 'upstream/develop' into develop
constantine2nd Oct 30, 2025
4f42180
feature/Rate Limiting endpoint tweaks 3
constantine2nd Oct 31, 2025
0e5a0b4
feature/Rate Limiting endpoint tweaks 4
constantine2nd Nov 4, 2025
d12dc94
feature/implementation of calendar-aligned rate limiting
constantine2nd Nov 4, 2025
5fd3d51
Revert "feature/implementation of calendar-aligned rate limiting"
constantine2nd Nov 4, 2025
9ff78b2
Revert "feature/Rate Limiting endpoint tweaks 4"
constantine2nd Nov 4, 2025
5f0cdf9
bugfix/fix the rate limiting issue
constantine2nd Nov 4, 2025
0ff6a48
Merge remote-tracking branch 'upstream/develop' into develop
constantine2nd Nov 4, 2025
1a241d1
test/Fix rate limiting v4.0.0 tests
constantine2nd Nov 5, 2025
4602818
feature/check NIST messages at startup
constantine2nd Nov 5, 2025
c8bfe81
feature/Revert rate limiting v3.1.0 and v4.0.0 paths
constantine2nd Nov 5, 2025
c158591
feature/Revert rate limiting v3.1.0 and v4.0.0 paths
constantine2nd Nov 5, 2025
6441341
Merge remote-tracking branch 'upstream/develop' into develop
constantine2nd Nov 5, 2025
747ac37
refactor/Removed 9 redundant patterns
constantine2nd Nov 5, 2025
c2e43a2
docfix/Remove outdated comments
constantine2nd Nov 5, 2025
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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -557,8 +557,8 @@ user_consumer_limit_anonymous_access=100, In case isn't defined default value is

Te set up Rate Limiting in case of the authorized access use these endpoints:

1. `GET ../management/consumers/CONSUMER_ID/consumer/call-limits` - Get Call Limits for a Consumer
2. `PUT ../management/consumers/CONSUMER_ID/consumer/call-limits` - Set Call Limits for a Consumer
1. `GET ../management/consumers/CONSUMER_ID/consumer/rate-limits` - Get Rate Limits for a Consumer
2. `PUT ../management/consumers/CONSUMER_ID/consumer/rate-limits` - Set Rate Limits for a Consumer

In order to make it work edit your props file in next way:

Expand Down
2 changes: 1 addition & 1 deletion completed_developments.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ Consent

Consumer

Get Call Limits for a Consumer
Get Rate Limits for a Consumer
Get Consumer
Get Consumers
Get Consumers (logged in User)
Expand Down
5 changes: 4 additions & 1 deletion obp-api/src/main/scala/code/api/util/APIUtil.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3119,7 +3119,10 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
} // Direct Login Deprecated i.e Authorization: DirectLogin token=eyJhbGciOiJIUzI1NiJ9.eyIiOiIifQ.Y0jk1EQGB4XgdqmYZUHT6potmH3mKj5mEaA9qrIXXWQ
else if (getPropsAsBoolValue("allow_direct_login", true) && hasDirectLoginHeader(cc.authReqHeaderField) && !url.contains("/my/logins/direct")) {
DirectLogin.getUserFromDirectLoginHeaderFuture(cc)
} else if (getPropsAsBoolValue("allow_direct_login", true) &&
}
// Endpoint /my/logins/direct is onboarding endpoint for Direct Login Flow authentication
// You POST your credentials (username, password, and consumer key) to the DirectLogin endpoint and receive a token in return.
else if (getPropsAsBoolValue("allow_direct_login", true) &&
(has2021DirectLoginHeader(cc.requestHeaders) || hasDirectLoginHeader(cc.authReqHeaderField)) &&
url.contains("/my/logins/direct")) {
Future{(Empty, Some(cc))}
Expand Down
60 changes: 35 additions & 25 deletions obp-api/src/main/scala/code/api/util/AfterApiAuth.scala
Original file line number Diff line number Diff line change
Expand Up @@ -96,38 +96,47 @@ object AfterApiAuth extends MdcLoggable{
* Please note that first source is the table RateLimiting and second is the table Consumer
*/
def checkRateLimiting(userIsLockedOrDeleted: Future[(Box[User], Option[CallContext])]): Future[(Box[User], Option[CallContext])] = {
def getRateLimiting(consumerId: String, version: String, name: String): Future[Box[RateLimiting]] = {
def getActiveRateLimitings(consumerId: String): Future[List[RateLimiting]] = {
RateLimitingUtil.useConsumerLimits match {
case true => RateLimitingDI.rateLimiting.vend.getByConsumerId(consumerId, version, name, Some(new Date()))
case false => Future(Empty)
case true => RateLimitingDI.rateLimiting.vend.getActiveCallLimitsByConsumerIdAtDate(consumerId, new Date())
case false => Future(List.empty)
}
}

def aggregateLimits(limits: List[RateLimiting], consumerId: String): CallLimit = {
def sumLimits(values: List[Long]): Long = {
val positiveValues = values.filter(_ > 0)
if (positiveValues.isEmpty) -1 else positiveValues.sum
}

if (limits.nonEmpty) {
CallLimit(
consumerId,
limits.find(_.apiName.isDefined).flatMap(_.apiName),
limits.find(_.apiVersion.isDefined).flatMap(_.apiVersion),
limits.find(_.bankId.isDefined).flatMap(_.bankId),
sumLimits(limits.map(_.perSecondCallLimit)),
sumLimits(limits.map(_.perMinuteCallLimit)),
sumLimits(limits.map(_.perHourCallLimit)),
sumLimits(limits.map(_.perDayCallLimit)),
sumLimits(limits.map(_.perWeekCallLimit)),
sumLimits(limits.map(_.perMonthCallLimit))
)
} else {
CallLimit(consumerId, None, None, None, -1, -1, -1, -1, -1, -1)
}
}

for {
(user, cc) <- userIsLockedOrDeleted
consumer = cc.flatMap(_.consumer)
version = cc.map(_.implementedInVersion).getOrElse("None") // Calculate apiVersion in case of Rate Limiting
operationId = cc.flatMap(_.operationId) // Unique Identifier of Dynamic Endpoints
// Calculate apiName in case of Rate Limiting
name = cc.flatMap(_.resourceDocument.map(_.partialFunctionName)) // 1st try: function name at resource doc
.orElse(operationId) // 2nd try: In case of Dynamic Endpoint we can only use operationId
.getOrElse("None") // Not found any unique identifier
rateLimiting <- getRateLimiting(consumer.map(_.consumerId.get).getOrElse(""), version, name)
consumerId = consumer.map(_.consumerId.get).getOrElse("")
rateLimitings <- getActiveRateLimitings(consumerId)
} yield {
val limit: Option[CallLimit] = rateLimiting match {
case Full(rl) => Some(CallLimit(
rl.consumerId,
rl.apiName,
rl.apiVersion,
rl.bankId,
rl.perSecondCallLimit,
rl.perMinuteCallLimit,
rl.perHourCallLimit,
rl.perDayCallLimit,
rl.perWeekCallLimit,
rl.perMonthCallLimit))
case Empty =>
val limit: Option[CallLimit] = rateLimitings match {
case Nil => // No rate limiting records found, use consumer defaults
Some(CallLimit(
consumer.map(_.consumerId.get).getOrElse(""),
consumerId,
None,
None,
None,
Expand All @@ -138,7 +147,8 @@ object AfterApiAuth extends MdcLoggable{
consumer.map(_.perWeekCallLimit.get).getOrElse(-1),
consumer.map(_.perMonthCallLimit.get).getOrElse(-1)
))
case _ => None
case activeLimits => // Aggregate multiple rate limiting records
Some(aggregateLimits(activeLimits, consumerId))
}
(user, cc.map(_.copy(rateLimiting = limit)))
}
Expand Down
Loading