diff --git a/README.md b/README.md index 376f5ef84..5beb3d69e 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/obp-api/pom.xml b/obp-api/pom.xml index e693e4de9..e9af941c3 100644 --- a/obp-api/pom.xml +++ b/obp-api/pom.xml @@ -318,12 +318,6 @@ scalameta_${scala.version} 3.7.4 - - - com.github.codemonstur - embedded-redis - 1.0.0 - diff --git a/obp-api/src/main/resources/props/sample.props.template b/obp-api/src/main/resources/props/sample.props.template index 9f5a199bd..048324436 100644 --- a/obp-api/src/main/resources/props/sample.props.template +++ b/obp-api/src/main/resources/props/sample.props.template @@ -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 diff --git a/obp-api/src/main/scala/code/api/util/RateLimitingUtil.scala b/obp-api/src/main/scala/code/api/util/RateLimitingUtil.scala index 03c7e22e8..1be4e88be 100644 --- a/obp-api/src/main/scala/code/api/util/RateLimitingUtil.scala +++ b/obp-api/src/main/scala/code/api/util/RateLimitingUtil.scala @@ -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 { diff --git a/obp-api/src/main/scala/code/api/v3_1_0/APIMethods310.scala b/obp-api/src/main/scala/code/api/v3_1_0/APIMethods310.scala index 2c11009fd..087ae000d 100644 --- a/obp-api/src/main/scala/code/api/v3_1_0/APIMethods310.scala +++ b/obp-api/src/main/scala/code/api/v3_1_0/APIMethods310.scala @@ -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)) diff --git a/obp-api/src/test/scala/code/api/v4_0_0/RateLimitingTest.scala b/obp-api/src/test/scala/code/api/v4_0_0/RateLimitingTest.scala index 1fde94fda..73ffe73e5 100644 --- a/obp-api/src/test/scala/code/api/v4_0_0/RateLimitingTest.scala +++ b/obp-api/src/test/scala/code/api/v4_0_0/RateLimitingTest.scala @@ -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)