mirror of
https://github.com/OpenBankProject/OBP-API.git
synced 2026-02-06 14:46:49 +00:00
feature/Remove in-memory mode and embedded redis in case of Rate Limiting
This commit is contained in:
parent
66bbf50768
commit
03bf9e4fc1
@ -405,10 +405,6 @@ There are two supported modes:
|
||||
|
||||
It is assumed that you have some Redis instance if you wan to use the functionality in multi node architecture.
|
||||
|
||||
To set up Rate Limiting in case of In-Memory mode edit your props file in next way:
|
||||
```
|
||||
use_consumer_limits_in_memory_mode=true
|
||||
```
|
||||
|
||||
We apply Rate Limiting for two type of access:
|
||||
* Authorized
|
||||
|
||||
@ -318,12 +318,6 @@
|
||||
<artifactId>scalameta_${scala.version}</artifactId>
|
||||
<version>3.7.4</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/com.github.codemonstur/embedded-redis -->
|
||||
<dependency>
|
||||
<groupId>com.github.codemonstur</groupId>
|
||||
<artifactId>embedded-redis</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/com.nimbusds/oauth2-oidc-sdk -->
|
||||
<dependency>
|
||||
|
||||
@ -798,8 +798,6 @@ featured_apis=elasticSearchWarehouseV300
|
||||
# Define how many calls per hour a consumer can make
|
||||
# In case isn't defined default value is "false"
|
||||
# use_consumer_limits=false
|
||||
# In case isn't defined default value is "false"
|
||||
# use_consumer_limits_in_memory_mode=false
|
||||
# In case isn't defined default value is 60
|
||||
# user_consumer_limit_anonymous_access=100
|
||||
# redis_address=127.0.0.1
|
||||
|
||||
@ -6,14 +6,11 @@ import code.api.util.ErrorMessages.TooManyRequests
|
||||
import code.api.util.RateLimitingJson.CallLimit
|
||||
import code.util.Helper.MdcLoggable
|
||||
import com.openbankproject.commons.model.User
|
||||
import net.liftweb.common.{Box, Empty, Full}
|
||||
import net.liftweb.util.Props
|
||||
import net.liftweb.common.{Box, Empty}
|
||||
import redis.clients.jedis.Jedis
|
||||
import redis.embedded.RedisServer
|
||||
|
||||
import scala.collection.immutable
|
||||
import scala.collection.immutable.{List, Nil}
|
||||
import scala.util.Random
|
||||
|
||||
|
||||
object RateLimitingPeriod extends Enumeration {
|
||||
@ -76,34 +73,10 @@ object RateLimitingUtil extends MdcLoggable {
|
||||
|
||||
val port = APIUtil.getPropsAsIntValue("redis_port", 6379)
|
||||
val url = APIUtil.getPropsValue("redis_address", "127.0.0.1")
|
||||
|
||||
private val mockedRedisPort = 6380 + Random.nextInt(20)
|
||||
private val mockedRedisHost = "127.0.0.1"
|
||||
private var server: RedisServer = null
|
||||
|
||||
def useConsumerLimits = APIUtil.getPropsAsBoolValue("use_consumer_limits", false)
|
||||
def inMemoryMode = APIUtil.getPropsAsBoolValue("use_consumer_limits_in_memory_mode", false)
|
||||
|
||||
lazy val jedis = Props.mode match {
|
||||
case Props.RunModes.Test =>
|
||||
startMockedRedis(mode="Test")
|
||||
case _ =>
|
||||
if(inMemoryMode == true) {
|
||||
startMockedRedis(mode="In-Memory")
|
||||
} else {
|
||||
new Jedis(url, port)
|
||||
}
|
||||
}
|
||||
|
||||
private def startMockedRedis(mode: String): Jedis = {
|
||||
import redis.clients.jedis.Jedis
|
||||
server = new RedisServer(mockedRedisPort)
|
||||
server.start()
|
||||
logger.info(msg = "-------------| Mocked Redis instance has been run in " + mode + " mode")
|
||||
logger.info(msg = "-------------| at host: " + mockedRedisHost)
|
||||
logger.info(msg = "-------------| at port: " + mockedRedisPort)
|
||||
new Jedis(mockedRedisHost, mockedRedisPort)
|
||||
}
|
||||
lazy val jedis = new Jedis(url, port)
|
||||
|
||||
def isRedisAvailable() = {
|
||||
try {
|
||||
@ -175,17 +148,8 @@ object RateLimitingUtil extends MdcLoggable {
|
||||
jedis.setex(key, seconds, "1")
|
||||
(seconds, 1)
|
||||
case _ => // otherwise increment the counter
|
||||
// TODO redis-mock has a bug "INCR clears TTL"
|
||||
inMemoryMode match {
|
||||
case true =>
|
||||
val cnt: Long = jedis.get(key).toLong + 1
|
||||
jedis.setex(key, ttl, String.valueOf(cnt))
|
||||
(ttl, cnt)
|
||||
case false =>
|
||||
val cnt = jedis.incr(key)
|
||||
(ttl, cnt)
|
||||
}
|
||||
|
||||
val cnt = jedis.incr(key)
|
||||
(ttl, cnt)
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
|
||||
@ -1242,15 +1242,9 @@ trait APIMethods310 {
|
||||
for {
|
||||
(_, callContext) <- anonymousAccess(cc)
|
||||
rateLimiting <- NewStyle.function.tryons("", 400, callContext) {
|
||||
RateLimitingUtil.inMemoryMode match {
|
||||
case true =>
|
||||
val isActive = if(RateLimitingUtil.useConsumerLimits == true) true else false
|
||||
RateLimiting(RateLimitingUtil.useConsumerLimits, "In-Memory", true, isActive)
|
||||
case false =>
|
||||
val isRedisAvailable = RateLimitingUtil.isRedisAvailable()
|
||||
val isActive = if(RateLimitingUtil.useConsumerLimits == true && isRedisAvailable == true) true else false
|
||||
RateLimiting(RateLimitingUtil.useConsumerLimits, "REDIS", isRedisAvailable, isActive)
|
||||
}
|
||||
val isRedisAvailable = RateLimitingUtil.isRedisAvailable()
|
||||
val isActive = if (RateLimitingUtil.useConsumerLimits && isRedisAvailable) true else false
|
||||
RateLimiting(RateLimitingUtil.useConsumerLimits, "REDIS", isRedisAvailable, isActive)
|
||||
}
|
||||
} yield {
|
||||
(createRateLimitingInfo(rateLimiting), HttpCode.`200`(callContext))
|
||||
|
||||
@ -25,23 +25,23 @@ TESOBE (http://www.tesobe.com/)
|
||||
*/
|
||||
package code.api.v4_0_0
|
||||
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.time.{ZoneId, ZonedDateTime}
|
||||
import java.util.Date
|
||||
import code.api.util.APIUtil.OAuth._
|
||||
import code.api.util.ApiRole.{CanSetCallLimits, canCreateDynamicEndpoint}
|
||||
import code.api.util.ErrorMessages.{UserHasMissingRoles, UserNotLoggedIn}
|
||||
import code.api.util.{APIUtil, ApiRole, ExampleValue}
|
||||
import code.api.util.{ApiRole, ExampleValue, RateLimitingUtil}
|
||||
import code.api.v3_0_0.OBPAPI3_0_0.Implementations3_0_0.getCurrentUser
|
||||
import code.api.v4_0_0.OBPAPI4_0_0.Implementations4_0_0
|
||||
import code.entitlement.Entitlement
|
||||
import code.setup.PropsReset
|
||||
import com.github.dwickern.macros.NameOf.nameOf
|
||||
import com.openbankproject.commons.model.ErrorMessage
|
||||
import com.openbankproject.commons.util.ApiVersion
|
||||
import net.liftweb.json.Serialization.write
|
||||
import org.scalatest.Tag
|
||||
import code.api.util.APIUtil.OAuth._
|
||||
import code.api.util.ApiVersionUtils.versions
|
||||
import code.setup.PropsReset
|
||||
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.time.{ZoneId, ZonedDateTime}
|
||||
import java.util.Date
|
||||
|
||||
class RateLimitingTest extends V400ServerSetup with PropsReset {
|
||||
|
||||
@ -92,6 +92,7 @@ class RateLimitingTest extends V400ServerSetup with PropsReset {
|
||||
feature("Rate Limit - " + ApiCallsLimit + " - " + ApiVersion400) {
|
||||
|
||||
scenario("We will try to set Rate Limiting per minute for a Consumer - unauthorized access", ApiCallsLimit, ApiVersion400) {
|
||||
assume(RateLimitingUtil.isRedisAvailable())
|
||||
When("We make a request v4.0.0")
|
||||
val response400 = setRateLimitingAnonymousAccess(callLimitJsonInitial)
|
||||
Then("We should get a 401")
|
||||
@ -100,6 +101,7 @@ class RateLimitingTest extends V400ServerSetup with PropsReset {
|
||||
response400.body.extract[ErrorMessage].message should equal (UserNotLoggedIn)
|
||||
}
|
||||
scenario("We will try to set Rate Limiting per minute without a proper Role " + ApiRole.canSetCallLimits, ApiCallsLimit, ApiVersion400) {
|
||||
assume(RateLimitingUtil.isRedisAvailable())
|
||||
When("We make a request v4.0.0 without a Role " + ApiRole.canSetCallLimits)
|
||||
val response400 = setRateLimitingWithoutRole(user1, callLimitJsonInitial)
|
||||
Then("We should get a 403")
|
||||
@ -108,6 +110,7 @@ class RateLimitingTest extends V400ServerSetup with PropsReset {
|
||||
response400.body.extract[ErrorMessage].message should equal (UserHasMissingRoles + CanSetCallLimits)
|
||||
}
|
||||
scenario("We will try to set Rate Limiting per minute with a proper Role " + ApiRole.canSetCallLimits, ApiCallsLimit, ApiVersion400) {
|
||||
assume(RateLimitingUtil.isRedisAvailable())
|
||||
When("We make a request v4.0.0 with a Role " + ApiRole.canSetCallLimits)
|
||||
val response400 = setRateLimiting(user1, callLimitJsonInitial)
|
||||
Then("We should get a 200")
|
||||
@ -115,7 +118,8 @@ class RateLimitingTest extends V400ServerSetup with PropsReset {
|
||||
response400.body.extract[CallLimitJsonV400]
|
||||
}
|
||||
scenario("We will set Rate Limiting per second for an Endpoint", ApiCallsLimit, ApiVersion400) {
|
||||
When("We make a request v4.0.0 with a Role " + ApiRole.canSetCallLimits)
|
||||
assume(RateLimitingUtil.isRedisAvailable())
|
||||
When("We make a request v4.0.0 with a Role " + ApiRole.canSetCallLimits)
|
||||
val response01 = setRateLimiting(user1, callLimitJsonSecond)
|
||||
Then("We should get a 200")
|
||||
response01.code should equal(200)
|
||||
@ -137,7 +141,8 @@ class RateLimitingTest extends V400ServerSetup with PropsReset {
|
||||
response04.code should equal(200)
|
||||
}
|
||||
scenario("We will set Rate Limiting per minute for an Endpoint", ApiCallsLimit, ApiVersion400) {
|
||||
When("We make a request v4.0.0 with a Role " + ApiRole.canSetCallLimits)
|
||||
assume(RateLimitingUtil.isRedisAvailable())
|
||||
When("We make a request v4.0.0 with a Role " + ApiRole.canSetCallLimits)
|
||||
val response01 = setRateLimiting(user1, callLimitJsonMinute)
|
||||
Then("We should get a 200")
|
||||
response01.code should equal(200)
|
||||
@ -158,7 +163,8 @@ class RateLimitingTest extends V400ServerSetup with PropsReset {
|
||||
response04.code should equal(200)
|
||||
}
|
||||
scenario("We will set Rate Limiting per hour for an Endpoint", ApiCallsLimit, ApiVersion400) {
|
||||
When("We make a request v4.0.0 with a Role " + ApiRole.canSetCallLimits)
|
||||
assume(RateLimitingUtil.isRedisAvailable())
|
||||
When("We make a request v4.0.0 with a Role " + ApiRole.canSetCallLimits)
|
||||
val response01 = setRateLimiting(user1, callLimitJsonHour)
|
||||
Then("We should get a 200")
|
||||
response01.code should equal(200)
|
||||
@ -179,7 +185,8 @@ class RateLimitingTest extends V400ServerSetup with PropsReset {
|
||||
response04.code should equal(200)
|
||||
}
|
||||
scenario("We will set Rate Limiting per week for an Endpoint", ApiCallsLimit, ApiVersion400) {
|
||||
When("We make a request v4.0.0 with a Role " + ApiRole.canSetCallLimits)
|
||||
assume(RateLimitingUtil.isRedisAvailable())
|
||||
When("We make a request v4.0.0 with a Role " + ApiRole.canSetCallLimits)
|
||||
val response01 = setRateLimiting(user1, callLimitJsonWeek)
|
||||
Then("We should get a 200")
|
||||
response01.code should equal(200)
|
||||
@ -200,7 +207,8 @@ class RateLimitingTest extends V400ServerSetup with PropsReset {
|
||||
response04.code should equal(200)
|
||||
}
|
||||
scenario("We will set Rate Limiting per month for an Endpoint", ApiCallsLimit, ApiVersion400) {
|
||||
When("We make a request v4.0.0 with a Role " + ApiRole.canSetCallLimits)
|
||||
assume(RateLimitingUtil.isRedisAvailable())
|
||||
When("We make a request v4.0.0 with a Role " + ApiRole.canSetCallLimits)
|
||||
val response01 = setRateLimiting(user1, callLimitJsonMonth)
|
||||
Then("We should get a 200")
|
||||
response01.code should equal(200)
|
||||
@ -224,7 +232,8 @@ class RateLimitingTest extends V400ServerSetup with PropsReset {
|
||||
|
||||
feature(s"Dynamic Endpoint: test $ApiCreateDynamicEndpoint version $ApiVersion400 - authorized access - with role - should be success!") {
|
||||
scenario("We will call the endpoint with user credentials", ApiCreateDynamicEndpoint, ApiVersion400) {
|
||||
When("We make a request v4.0.0")
|
||||
assume(RateLimitingUtil.isRedisAvailable())
|
||||
When("We make a request v4.0.0")
|
||||
val postDynamicEndpointRequestBodyExample = ExampleValue.dynamicEndpointRequestBodyExample
|
||||
When("We make a request v4.0.0")
|
||||
val request = (v4_0_0_Request / "management" / "dynamic-endpoints").POST<@ (user1)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user