diff --git a/README.md b/README.md index 54882e5444..c045d4540f 100644 --- a/README.md +++ b/README.md @@ -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: diff --git a/completed_developments.md b/completed_developments.md index 314d55f1a2..e6b8a46a9b 100644 --- a/completed_developments.md +++ b/completed_developments.md @@ -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) diff --git a/obp-api/src/main/scala/code/api/util/APIUtil.scala b/obp-api/src/main/scala/code/api/util/APIUtil.scala index 1564734f44..6351120068 100644 --- a/obp-api/src/main/scala/code/api/util/APIUtil.scala +++ b/obp-api/src/main/scala/code/api/util/APIUtil.scala @@ -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))} diff --git a/obp-api/src/main/scala/code/api/util/AfterApiAuth.scala b/obp-api/src/main/scala/code/api/util/AfterApiAuth.scala index 0fc70591c1..13eae4fc40 100644 --- a/obp-api/src/main/scala/code/api/util/AfterApiAuth.scala +++ b/obp-api/src/main/scala/code/api/util/AfterApiAuth.scala @@ -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, @@ -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))) } diff --git a/obp-api/src/main/scala/code/api/util/ApiRole.scala b/obp-api/src/main/scala/code/api/util/ApiRole.scala index d07208a039..c711c22633 100644 --- a/obp-api/src/main/scala/code/api/util/ApiRole.scala +++ b/obp-api/src/main/scala/code/api/util/ApiRole.scala @@ -480,15 +480,18 @@ object ApiRole extends MdcLoggable{ case class CanReadUserLockedStatus(requiresBankId: Boolean = false) extends ApiRole lazy val canReadUserLockedStatus = CanReadUserLockedStatus() - case class CanSetCallLimits(requiresBankId: Boolean = false) extends ApiRole - lazy val canSetCallLimits = CanSetCallLimits() + case class CanUpdateRateLimits(requiresBankId: Boolean = false) extends ApiRole + lazy val canUpdateRateLimits = CanUpdateRateLimits() + + case class CanCreateRateLimits(requiresBankId: Boolean = false) extends ApiRole + lazy val canCreateRateLimits = CanCreateRateLimits() case class CanDeleteRateLimiting(requiresBankId: Boolean = false) extends ApiRole - lazy val canDeleteRateLimiting = CanDeleteRateLimiting() - + lazy val canDeleteRateLimits = CanDeleteRateLimiting() + case class CanCreateCustomerMessage(requiresBankId: Boolean = true) extends ApiRole - lazy val canCreateCustomerMessage = CanCreateCustomerMessage() - + lazy val canCreateCustomerMessage = CanCreateCustomerMessage() + case class CanGetCustomerMessages(requiresBankId: Boolean = true) extends ApiRole lazy val canGetCustomerMessages = CanGetCustomerMessages() @@ -503,10 +506,10 @@ object ApiRole extends MdcLoggable{ case class CanCreateSystemAccountNotificationWebhook(requiresBankId: Boolean = false) extends ApiRole lazy val canCreateSystemAccountNotificationWebhook = CanCreateSystemAccountNotificationWebhook() - + case class CanCreateAccountNotificationWebhookAtOneBank(requiresBankId: Boolean = true) extends ApiRole lazy val canCreateAccountNotificationWebhookAtOneBank = CanCreateAccountNotificationWebhookAtOneBank() - + case class CanUpdateWebhook(requiresBankId: Boolean = true) extends ApiRole lazy val canUpdateWebhook = CanUpdateWebhook() @@ -551,22 +554,22 @@ object ApiRole extends MdcLoggable{ case class CanUpdateProductAttribute(requiresBankId: Boolean = true) extends ApiRole lazy val canUpdateProductAttribute = CanUpdateProductAttribute() - + case class CanUpdateBankAttribute(requiresBankId: Boolean = true) extends ApiRole - lazy val canUpdateBankAttribute = CanUpdateBankAttribute() - + lazy val canUpdateBankAttribute = CanUpdateBankAttribute() + case class CanUpdateAtmAttribute(requiresBankId: Boolean = true) extends ApiRole - lazy val canUpdateAtmAttribute = CanUpdateAtmAttribute() - + lazy val canUpdateAtmAttribute = CanUpdateAtmAttribute() + case class CanUpdateAtmAttributeAtAnyBank(requiresBankId: Boolean = false) extends ApiRole lazy val canUpdateAtmAttributeAtAnyBank = CanUpdateAtmAttributeAtAnyBank() - + case class CanGetBankAttribute(requiresBankId: Boolean = true) extends ApiRole - lazy val canGetBankAttribute = CanGetBankAttribute() - + lazy val canGetBankAttribute = CanGetBankAttribute() + case class CanGetAtmAttribute(requiresBankId: Boolean = true) extends ApiRole - lazy val canGetAtmAttribute = CanGetAtmAttribute() - + lazy val canGetAtmAttribute = CanGetAtmAttribute() + case class CanGetAtmAttributeAtAnyBank(requiresBankId: Boolean = false) extends ApiRole lazy val canGetAtmAttributeAtAnyBank = CanGetAtmAttributeAtAnyBank() @@ -575,25 +578,25 @@ object ApiRole extends MdcLoggable{ case class CanDeleteProductAttribute(requiresBankId: Boolean = true) extends ApiRole lazy val canDeleteProductAttribute = CanDeleteProductAttribute() - + case class CanDeleteBankAttribute(requiresBankId: Boolean = true) extends ApiRole - lazy val canDeleteBankAttribute = CanDeleteBankAttribute() - + lazy val canDeleteBankAttribute = CanDeleteBankAttribute() + case class CanDeleteAtmAttribute(requiresBankId: Boolean = true) extends ApiRole lazy val canDeleteAtmAttribute = CanDeleteAtmAttribute() - + case class CanDeleteAtmAttributeAtAnyBank(requiresBankId: Boolean = false) extends ApiRole lazy val canDeleteAtmAttributeAtAnyBank = CanDeleteAtmAttributeAtAnyBank() case class CanCreateProductAttribute(requiresBankId: Boolean = true) extends ApiRole lazy val canCreateProductAttribute = CanCreateProductAttribute() - + case class CanCreateBankAttribute(requiresBankId: Boolean = true) extends ApiRole lazy val canCreateBankAttribute = CanCreateBankAttribute() - + case class CanCreateAtmAttribute(requiresBankId: Boolean = true) extends ApiRole - lazy val canCreateAtmAttribute = CanCreateAtmAttribute() - + lazy val canCreateAtmAttribute = CanCreateAtmAttribute() + case class CanCreateAtmAttributeAtAnyBank(requiresBankId: Boolean = false) extends ApiRole lazy val canCreateAtmAttributeAtAnyBank = CanCreateAtmAttributeAtAnyBank() @@ -608,7 +611,7 @@ object ApiRole extends MdcLoggable{ case class CanCreateProductFee(requiresBankId: Boolean = true) extends ApiRole lazy val canCreateProductFee = CanCreateProductFee() - + case class CanMaintainProductCollection(requiresBankId: Boolean = true) extends ApiRole lazy val canMaintainProductCollection = CanMaintainProductCollection() @@ -669,10 +672,10 @@ object ApiRole extends MdcLoggable{ case class CanCreateBankLevelDynamicEntity(requiresBankId: Boolean = true) extends ApiRole lazy val canCreateBankLevelDynamicEntity = CanCreateBankLevelDynamicEntity() - + case class CanUpdateSystemLevelDynamicEntity(requiresBankId: Boolean = false) extends ApiRole lazy val canUpdateSystemDynamicEntity = CanUpdateSystemLevelDynamicEntity() - + case class CanUpdateBankLevelDynamicEntity(requiresBankId: Boolean = true) extends ApiRole lazy val canUpdateBankLevelDynamicEntity = CanUpdateBankLevelDynamicEntity() @@ -687,13 +690,13 @@ object ApiRole extends MdcLoggable{ case class CanGetDynamicEndpoint(requiresBankId: Boolean = false) extends ApiRole lazy val canGetDynamicEndpoint = CanGetDynamicEndpoint() - + case class CanGetDynamicEndpoints(requiresBankId: Boolean = false) extends ApiRole lazy val canGetDynamicEndpoints = CanGetDynamicEndpoints() case class CanGetBankLevelDynamicEndpoint(requiresBankId: Boolean = true) extends ApiRole lazy val canGetBankLevelDynamicEndpoint = CanGetBankLevelDynamicEndpoint() - + case class CanGetBankLevelDynamicEndpoints(requiresBankId: Boolean = true) extends ApiRole lazy val canGetBankLevelDynamicEndpoints = CanGetBankLevelDynamicEndpoints() @@ -714,7 +717,7 @@ object ApiRole extends MdcLoggable{ case class CanDeleteBankLevelDynamicEndpoint(requiresBankId: Boolean = true) extends ApiRole lazy val canDeleteBankLevelDynamicEndpoint = CanDeleteBankLevelDynamicEndpoint() - + case class CanCreateResetPasswordUrl(requiresBankId: Boolean = false) extends ApiRole lazy val canCreateResetPasswordUrl = CanCreateResetPasswordUrl() @@ -744,7 +747,7 @@ object ApiRole extends MdcLoggable{ case class CanCreateDirectDebitAtOneBank(requiresBankId: Boolean = true) extends ApiRole lazy val canCreateDirectDebitAtOneBank = CanCreateDirectDebitAtOneBank() - + case class CanCreateStandingOrderAtOneBank(requiresBankId: Boolean = true) extends ApiRole lazy val canCreateStandingOrderAtOneBank = CanCreateStandingOrderAtOneBank() @@ -762,7 +765,7 @@ object ApiRole extends MdcLoggable{ case class CanDeleteCustomerAttributeAtOneBank(requiresBankId: Boolean = true) extends ApiRole lazy val canDeleteCustomerAttributeAtOneBank = CanDeleteCustomerAttributeAtOneBank() - + case class CanDeleteCustomerAttributeAtAnyBank(requiresBankId: Boolean = false) extends ApiRole lazy val canDeleteCustomerAttributeAtAnyBank = CanDeleteCustomerAttributeAtAnyBank() @@ -810,61 +813,61 @@ object ApiRole extends MdcLoggable{ case class CanGetDoubleEntryTransactionAtOneBank(requiresBankId: Boolean = true) extends ApiRole lazy val canGetDoubleEntryTransactionAtOneBank = CanGetDoubleEntryTransactionAtOneBank() - + case class CanGetDoubleEntryTransactionAtAnyBank(requiresBankId: Boolean = false) extends ApiRole lazy val canGetDoubleEntryTransactionAtAnyBank = CanGetDoubleEntryTransactionAtAnyBank() case class CanReadResourceDoc(requiresBankId: Boolean = false) extends ApiRole lazy val canReadResourceDoc = CanReadResourceDoc() - + case class CanReadStaticResourceDoc(requiresBankId: Boolean = false) extends ApiRole lazy val canReadStaticResourceDoc = CanReadStaticResourceDoc() - + case class CanReadDynamicResourceDocsAtOneBank(requiresBankId: Boolean = true) extends ApiRole lazy val canReadDynamicResourceDocsAtOneBank = CanReadDynamicResourceDocsAtOneBank() - + case class CanReadGlossary(requiresBankId: Boolean = false) extends ApiRole lazy val canReadGlossary = CanReadGlossary() case class CanCreateCustomerAttributeDefinitionAtOneBank(requiresBankId: Boolean = true) extends ApiRole lazy val canCreateCustomerAttributeDefinitionAtOneBank = CanCreateCustomerAttributeDefinitionAtOneBank() - + case class CanDeleteCustomerAttributeDefinitionAtOneBank(requiresBankId: Boolean = true) extends ApiRole lazy val canDeleteCustomerAttributeDefinitionAtOneBank = CanDeleteCustomerAttributeDefinitionAtOneBank() - + case class CanGetCustomerAttributeDefinitionAtOneBank(requiresBankId: Boolean = true) extends ApiRole lazy val canGetCustomerAttributeDefinitionAtOneBank = CanGetCustomerAttributeDefinitionAtOneBank() - + case class CanCreateAccountAttributeDefinitionAtOneBank(requiresBankId: Boolean = true) extends ApiRole - lazy val canCreateAccountAttributeDefinitionAtOneBank = CanCreateAccountAttributeDefinitionAtOneBank() - + lazy val canCreateAccountAttributeDefinitionAtOneBank = CanCreateAccountAttributeDefinitionAtOneBank() + case class CanDeleteAccountAttributeDefinitionAtOneBank(requiresBankId: Boolean = true) extends ApiRole lazy val canDeleteAccountAttributeDefinitionAtOneBank = CanDeleteAccountAttributeDefinitionAtOneBank() - + case class CanGetAccountAttributeDefinitionAtOneBank(requiresBankId: Boolean = true) extends ApiRole - lazy val canGetAccountAttributeDefinitionAtOneBank = CanGetAccountAttributeDefinitionAtOneBank() - + lazy val canGetAccountAttributeDefinitionAtOneBank = CanGetAccountAttributeDefinitionAtOneBank() + case class CanDeleteProductAttributeDefinitionAtOneBank(requiresBankId: Boolean = true) extends ApiRole - lazy val canDeleteProductAttributeDefinitionAtOneBank = CanDeleteProductAttributeDefinitionAtOneBank() - + lazy val canDeleteProductAttributeDefinitionAtOneBank = CanDeleteProductAttributeDefinitionAtOneBank() + case class CanGetProductAttributeDefinitionAtOneBank(requiresBankId: Boolean = true) extends ApiRole lazy val canGetProductAttributeDefinitionAtOneBank = CanGetProductAttributeDefinitionAtOneBank() - + case class CanCreateProductAttributeDefinitionAtOneBank(requiresBankId: Boolean = true) extends ApiRole lazy val canCreateProductAttributeDefinitionAtOneBank = CanCreateProductAttributeDefinitionAtOneBank() - + case class CanCreateBankAttributeDefinitionAtOneBank(requiresBankId: Boolean = true) extends ApiRole lazy val canCreateBankAttributeDefinitionAtOneBank = CanCreateBankAttributeDefinitionAtOneBank() - + case class CanCreateTransactionAttributeDefinitionAtOneBank(requiresBankId: Boolean = true) extends ApiRole lazy val canCreateTransactionAttributeDefinitionAtOneBank = CanCreateTransactionAttributeDefinitionAtOneBank() - + case class CanDeleteTransactionAttributeDefinitionAtOneBank(requiresBankId: Boolean = true) extends ApiRole lazy val canDeleteTransactionAttributeDefinitionAtOneBank = CanDeleteTransactionAttributeDefinitionAtOneBank() - + case class CanGetTransactionAttributeDefinitionAtOneBank(requiresBankId: Boolean = true) extends ApiRole - lazy val canGetTransactionAttributeDefinitionAtOneBank = CanGetTransactionAttributeDefinitionAtOneBank() - + lazy val canGetTransactionAttributeDefinitionAtOneBank = CanGetTransactionAttributeDefinitionAtOneBank() + case class CanCreateTransactionRequestAttributeDefinitionAtOneBank(requiresBankId: Boolean = true) extends ApiRole lazy val canCreateTransactionRequestAttributeDefinitionAtOneBank = CanCreateTransactionRequestAttributeDefinitionAtOneBank() @@ -882,19 +885,19 @@ object ApiRole extends MdcLoggable{ case class CanCreateCardAttributeDefinitionAtOneBank(requiresBankId: Boolean = true) extends ApiRole lazy val canCreateCardAttributeDefinitionAtOneBank = CanCreateCardAttributeDefinitionAtOneBank() - + case class CanDeleteTransactionCascade(requiresBankId: Boolean = true) extends ApiRole lazy val canDeleteTransactionCascade = CanDeleteTransactionCascade() - + case class CanDeleteAccountCascade(requiresBankId: Boolean = true) extends ApiRole - lazy val canDeleteAccountCascade = CanDeleteAccountCascade() - + lazy val canDeleteAccountCascade = CanDeleteAccountCascade() + case class CanDeleteBankCascade(requiresBankId: Boolean = true) extends ApiRole lazy val canDeleteBankCascade = CanDeleteBankCascade() - + case class CanDeleteProductCascade(requiresBankId: Boolean = true) extends ApiRole - lazy val canDeleteProductCascade = CanDeleteProductCascade() - + lazy val canDeleteProductCascade = CanDeleteProductCascade() + case class CanDeleteCustomerCascade(requiresBankId: Boolean = true) extends ApiRole lazy val canDeleteCustomerCascade = CanDeleteCustomerCascade() @@ -933,10 +936,10 @@ object ApiRole extends MdcLoggable{ case class CanUpdateConnectorMethod(requiresBankId: Boolean = false) extends ApiRole lazy val canUpdateConnectorMethod = CanUpdateConnectorMethod() - + case class CanGetAllConnectorMethods(requiresBankId: Boolean = false) extends ApiRole lazy val canGetAllConnectorMethods = CanGetAllConnectorMethods() - + case class CanCreateDynamicResourceDoc(requiresBankId: Boolean = false) extends ApiRole lazy val canCreateDynamicResourceDoc = CanCreateDynamicResourceDoc() @@ -951,7 +954,7 @@ object ApiRole extends MdcLoggable{ case class CanDeleteDynamicResourceDoc(requiresBankId: Boolean = false) extends ApiRole lazy val canDeleteDynamicResourceDoc = CanDeleteDynamicResourceDoc() - + case class CanCreateBankLevelDynamicResourceDoc(requiresBankId: Boolean = true) extends ApiRole lazy val canCreateBankLevelDynamicResourceDoc = CanCreateBankLevelDynamicResourceDoc() @@ -969,7 +972,7 @@ object ApiRole extends MdcLoggable{ case class CanCreateDynamicMessageDoc(requiresBankId: Boolean = false) extends ApiRole lazy val canCreateDynamicMessageDoc = CanCreateDynamicMessageDoc() - + case class CanCreateBankLevelDynamicMessageDoc(requiresBankId: Boolean = true) extends ApiRole lazy val canCreateBankLevelDynamicMessageDoc = CanCreateBankLevelDynamicMessageDoc() @@ -1020,21 +1023,21 @@ object ApiRole extends MdcLoggable{ case class CanDeleteBankLevelEndpointMapping(requiresBankId: Boolean = true) extends ApiRole lazy val canDeleteBankLevelEndpointMapping = CanDeleteBankLevelEndpointMapping() - + case class CanCreateUserInvitation(requiresBankId: Boolean = true) extends ApiRole - lazy val canCreateUserInvitation = CanCreateUserInvitation() + lazy val canCreateUserInvitation = CanCreateUserInvitation() case class CanGetUserInvitation(requiresBankId: Boolean = true) extends ApiRole lazy val canGetUserInvitation = CanGetUserInvitation() case class CanCreateSystemLevelEndpointTag(requiresBankId: Boolean = false) extends ApiRole lazy val canCreateSystemLevelEndpointTag = CanCreateSystemLevelEndpointTag() - + case class CanUpdateSystemLevelEndpointTag(requiresBankId: Boolean = false) extends ApiRole lazy val canUpdateSystemLevelEndpointTag = CanUpdateSystemLevelEndpointTag() - + case class CanDeleteSystemLevelEndpointTag(requiresBankId: Boolean = false) extends ApiRole lazy val canDeleteSystemLevelEndpointTag = CanDeleteSystemLevelEndpointTag() - + case class CanGetSystemLevelEndpointTag(requiresBankId: Boolean = false) extends ApiRole lazy val canGetSystemLevelEndpointTag = CanGetSystemLevelEndpointTag() @@ -1071,7 +1074,7 @@ object ApiRole extends MdcLoggable{ case class CanGetAccountsMinimalForCustomerAtAnyBank(requiresBankId: Boolean = false) extends ApiRole lazy val canGetAccountsMinimalForCustomerAtAnyBank = CanGetAccountsMinimalForCustomerAtAnyBank() - + case class CanUpdateConsentStatusAtOneBank(requiresBankId: Boolean = true) extends ApiRole lazy val canUpdateConsentStatusAtOneBank = CanUpdateConsentStatusAtOneBank() case class CanUpdateConsentStatusAtAnyBank(requiresBankId: Boolean = false) extends ApiRole @@ -1096,7 +1099,7 @@ object ApiRole extends MdcLoggable{ case class CanGetSystemIntegrity(requiresBankId: Boolean = false) extends ApiRole lazy val canGetSystemIntegrity = CanGetSystemIntegrity() - + private val dynamicApiRoles = new ConcurrentHashMap[String, ApiRole] private case class DynamicApiRole(role: String, requiresBankId: Boolean = false) extends ApiRole{ @@ -1142,35 +1145,35 @@ object ApiRole extends MdcLoggable{ } object Util { - + def checkWrongDefinedNames: List[List[Unit]] = { import scala.meta._ val source: Source = new java.io.File("obp-api/src/main/scala/code/api/util/ApiRole.scala").parse[Source].get - val allowedPrefixes = + val allowedPrefixes = List( "CanCreate", - "CanGet", - "CanUpdate", - "CanDelete", - "CanMaintain", - "CanSearch", - "CanEnable", + "CanGet", + "CanUpdate", + "CanDelete", + "CanMaintain", + "CanSearch", + "CanEnable", "CanDisable" ) - val allowedExistingNames = + val allowedExistingNames = List( "CanQueryOtherUser", - "CanAddSocialMediaHandle", - "CanReadMetrics", - "CanUseFirehoseAtAnyBank", - "CanReadAggregateMetrics", - "CanUnlockUser", - "CanReadUserLockedStatus", - "CanReadCallLimits", - "CanCheckFundsAvailable", - "CanRefreshUser", - "CanReadFx", + "CanAddSocialMediaHandle", + "CanReadMetrics", + "CanUseFirehoseAtAnyBank", + "CanReadAggregateMetrics", + "CanUnlockUser", + "CanReadUserLockedStatus", + "CanReadCallLimits", + "CanCheckFundsAvailable", + "CanRefreshUser", + "CanReadFx", "CanSetCallLimits", "CanDeleteRateLimiting" ) diff --git a/obp-api/src/main/scala/code/api/util/ApiSession.scala b/obp-api/src/main/scala/code/api/util/ApiSession.scala index d6f5774911..71d60b9eca 100644 --- a/obp-api/src/main/scala/code/api/util/ApiSession.scala +++ b/obp-api/src/main/scala/code/api/util/ApiSession.scala @@ -12,6 +12,7 @@ import code.context.UserAuthContextProvider import code.customer.CustomerX import code.model.{Consumer, _} import code.util.Helper.MdcLoggable +import code.util.SecureLogging import code.views.Views import com.openbankproject.commons.model._ import com.openbankproject.commons.util.{EnumValue, OBPEnumeration} @@ -56,7 +57,9 @@ case class CallContext( paginationOffset : Option[String] = None, paginationLimit : Option[String] = None ) extends MdcLoggable { - + override def toString: String = SecureLogging.maskSensitive( + s"${this.getClass.getSimpleName}(${this.productIterator.mkString(", ")})" + ) //This is only used to connect the back adapter. not useful for sandbox mode. def toOutboundAdapterCallContext: OutboundAdapterCallContext= { for{ 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 283cb12640..abc3f17f47 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 @@ -509,11 +509,11 @@ trait APIMethods310 { nameOf(callsLimit), "PUT", "/management/consumers/CONSUMER_ID/consumer/call-limits", - "Set Rate Limiting (call limits) per Consumer", + "Set Rate Limits (call limits) per Consumer", s""" |Set the API rate limiting (call limits) per Consumer: | - |Call limits can be set: + |Rate limits can be set: | |Per Second |Per Minute @@ -537,14 +537,14 @@ trait APIMethods310 { UnknownError ), List(apiTagConsumer), - Some(List(canSetCallLimits))) + Some(List(canUpdateRateLimits))) lazy val callsLimit : OBPEndpoint = { case "management" :: "consumers" :: consumerId :: "consumer" :: "call-limits" :: Nil JsonPut json -> _ => { cc => implicit val ec = EndpointContext(Some(cc)) for { (Full(u), callContext) <- authenticatedAccess(cc) - _ <- NewStyle.function.hasEntitlement("", u.userId, canSetCallLimits, callContext) + _ <- NewStyle.function.hasEntitlement("", u.userId, canUpdateRateLimits, callContext) postJson <- NewStyle.function.tryons(s"$InvalidJsonFormat The Json body should be the $CallLimitPostJson ", 400, callContext) { json.extract[CallLimitPostJson] } @@ -578,9 +578,9 @@ trait APIMethods310 { nameOf(getCallsLimit), "GET", "/management/consumers/CONSUMER_ID/consumer/call-limits", - "Get Call Limits for a Consumer", + "Get Rate Limits for a Consumer", s""" - |Get Calls limits per Consumer. + |Get Rate Limits per Consumer. |${userAuthenticationMessage(true)} | |""".stripMargin, @@ -596,7 +596,7 @@ trait APIMethods310 { UnknownError ), List(apiTagConsumer), - Some(List(canSetCallLimits))) + Some(List(canUpdateRateLimits))) diff --git a/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala b/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala index db729eb4ae..3f5b94eec0 100644 --- a/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala +++ b/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala @@ -202,14 +202,14 @@ trait APIMethods400 extends MdcLoggable { UnknownError ), List(apiTagConsumer, apiTagRateLimits), - Some(List(canSetCallLimits))) + Some(List(canUpdateRateLimits))) lazy val callsLimit : OBPEndpoint = { case "management" :: "consumers" :: consumerId :: "consumer" :: "call-limits" :: Nil JsonPut json -> _ => { cc => implicit val ec = EndpointContext(Some(cc)) for { (Full(u), callContext) <- authenticatedAccess(cc) - _ <- NewStyle.function.handleEntitlementsAndScopes("", u.userId, List(canSetCallLimits), callContext) + _ <- NewStyle.function.handleEntitlementsAndScopes("", u.userId, List(canUpdateRateLimits), callContext) postJson <- NewStyle.function.tryons(s"$InvalidJsonFormat The Json body should be the $CallLimitPostJsonV400 ", 400, callContext) { json.extract[CallLimitPostJsonV400] } @@ -3578,8 +3578,8 @@ trait APIMethods400 extends MdcLoggable { |The user creating this will be automatically assigned the Role CanCreateEntitlementAtOneBank. |Thus the User can manage the bank they create and assign Roles to other Users. | - |Only SANDBOX mode - |The settlement accounts are created specified by the bank in the POST body. + |Only SANDBOX mode (i.e. when connector=mapped in properties file) + |The settlement accounts are automatically created by the system when the bank is created. |Name and account id are created in accordance to the next rules: | - Incoming account (name: Default incoming settlement account, Account ID: OBP_DEFAULT_INCOMING_ACCOUNT_ID, currency: EUR) | - Outgoing account (name: Default outgoing settlement account, Account ID: OBP_DEFAULT_OUTGOING_ACCOUNT_ID, currency: EUR) diff --git a/obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala b/obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala index 9a23dac09e..f985b2402c 100644 --- a/obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala +++ b/obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala @@ -153,8 +153,8 @@ trait APIMethods500 { |The user creating this will be automatically assigned the Role CanCreateEntitlementAtOneBank. |Thus the User can manage the bank they create and assign Roles to other Users. | - |Only SANDBOX mode - |The settlement accounts are created specified by the bank in the POST body. + |Only SANDBOX mode (i.e. when connector=mapped in properties file) + |The settlement accounts are automatically created by the system when the bank is created. |Name and account id are created in accordance to the next rules: | - Incoming account (name: Default incoming settlement account, Account ID: OBP_DEFAULT_INCOMING_ACCOUNT_ID, currency: EUR) | - Outgoing account (name: Default outgoing settlement account, Account ID: OBP_DEFAULT_OUTGOING_ACCOUNT_ID, currency: EUR) diff --git a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala index 82bbbe2c4d..8805a93313 100644 --- a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala +++ b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala @@ -3329,8 +3329,8 @@ trait APIMethods510 { implementedInApiVersion, nameOf(getCallsLimit), "GET", - "/management/consumers/CONSUMER_ID/consumer/call-limits", - "Get Call Limits for a Consumer", + "/management/consumers/CONSUMER_ID/consumer/rate-limits", + "Get Rate Limits for a Consumer", s""" |Get Calls limits per Consumer. |${userAuthenticationMessage(true)} @@ -3352,7 +3352,7 @@ trait APIMethods510 { lazy val getCallsLimit: OBPEndpoint = { - case "management" :: "consumers" :: consumerId :: "consumer" :: "call-limits" :: Nil JsonGet _ => + case "management" :: "consumers" :: consumerId :: "consumer" :: "rate-limits" :: Nil JsonGet _ => cc => implicit val ec = EndpointContext(Some(cc)) for { diff --git a/obp-api/src/main/scala/code/api/v6_0_0/APIMethods600.scala b/obp-api/src/main/scala/code/api/v6_0_0/APIMethods600.scala index 4eb9a5bb3e..7eceafc774 100644 --- a/obp-api/src/main/scala/code/api/v6_0_0/APIMethods600.scala +++ b/obp-api/src/main/scala/code/api/v6_0_0/APIMethods600.scala @@ -11,6 +11,8 @@ import code.api.util.FutureUtil.EndpointContext import code.api.util.NewStyle.HttpCode import code.api.util.{APIUtil, ErrorMessages, NewStyle, RateLimitingUtil} import code.api.v3_0_0.JSONFactory300 +import code.api.v4_0_0.CallLimitPostJsonV400 +import code.api.v4_0_0.JSONFactory400.createCallsLimitJson import code.api.v5_0_0.JSONFactory500 import code.api.v6_0_0.JSONFactory600.{createActiveCallLimitsJsonV600, createCallLimitJsonV600, createCurrentUsageJson} import code.bankconnectors.LocalMappedConnectorInternal @@ -145,16 +147,16 @@ trait APIMethods600 { (accountsJson, HttpCode.`200`(callContext)) } } - + staticResourceDocs += ResourceDoc( getCurrentCallsLimit, implementedInApiVersion, nameOf(getCurrentCallsLimit), "GET", "/management/consumers/CONSUMER_ID/consumer/current-usage", - "Get Call Limits for a Consumer Usage", + "Get Rate Limits for a Consumer Usage", s""" - |Get Call Limits for a Consumer Usage. + |Get Rate Limits for a Consumer Usage. |${userAuthenticationMessage(true)} | |""".stripMargin, @@ -191,10 +193,10 @@ trait APIMethods600 { implementedInApiVersion, nameOf(createCallLimits), "POST", - "/management/consumers/CONSUMER_ID/consumer/call-limits", - "Create Call Limits for a Consumer", + "/management/consumers/CONSUMER_ID/consumer/rate-limits", + "Create Rate Limits for a Consumer", s""" - |Create Call Limits for a Consumer + |Create Rate Limits for a Consumer | |${userAuthenticationMessage(true)} | @@ -210,21 +212,21 @@ trait APIMethods600 { UnknownError ), List(apiTagConsumer), - Some(List(canSetCallLimits))) + Some(List(canCreateRateLimits))) lazy val createCallLimits: OBPEndpoint = { - case "management" :: "consumers" :: consumerId :: "consumer" :: "call-limits" :: Nil JsonPost json -> _ => + case "management" :: "consumers" :: consumerId :: "consumer" :: "rate-limits" :: Nil JsonPost json -> _ => cc => implicit val ec = EndpointContext(Some(cc)) for { (Full(u), callContext) <- authenticatedAccess(cc) - _ <- NewStyle.function.hasEntitlement("", u.userId, canSetCallLimits, callContext) + _ <- NewStyle.function.hasEntitlement("", u.userId, canCreateRateLimits, callContext) postJson <- NewStyle.function.tryons(s"$InvalidJsonFormat The Json body should be the $CallLimitPostJsonV600 ", 400, callContext) { json.extract[CallLimitPostJsonV600] } _ <- NewStyle.function.getConsumerByConsumerId(consumerId, callContext) - rateLimiting <- RateLimitingDI.rateLimiting.vend.createOrUpdateConsumerCallLimits( + rateLimiting <- RateLimitingDI.rateLimiting.vend.createConsumerCallLimits( consumerId, postJson.from_date, postJson.to_date, @@ -247,15 +249,84 @@ trait APIMethods600 { } + staticResourceDocs += ResourceDoc( + updateRateLimits, + implementedInApiVersion, + nameOf(updateRateLimits), + "PUT", + "/management/consumers/CONSUMER_ID/consumer/rate-limits/RATE_LIMITING_ID", + "Set Rate Limits / Call Limits per Consumer", + s""" + |Set the API rate limits / call limits for a Consumer: + | + |Rate limiting can be set: + | + |Per Second + |Per Minute + |Per Hour + |Per Week + |Per Month + | + | + |${userAuthenticationMessage(true)} + | + |""".stripMargin, + callLimitPostJsonV400, + callLimitPostJsonV400, + List( + UserNotLoggedIn, + InvalidJsonFormat, + InvalidConsumerId, + ConsumerNotFoundByConsumerId, + UserHasMissingRoles, + UpdateConsumerError, + UnknownError + ), + List(apiTagConsumer, apiTagRateLimits), + Some(List(canUpdateRateLimits))) + + lazy val updateRateLimits: OBPEndpoint = { + case "management" :: "consumers" :: consumerId :: "consumer" :: "rate-limits" :: rateLimitingId :: Nil JsonPut json -> _ => { + cc => + implicit val ec = EndpointContext(Some(cc)) + for { + (Full(u), callContext) <- authenticatedAccess(cc) + _ <- NewStyle.function.handleEntitlementsAndScopes("", u.userId, List(canUpdateRateLimits), callContext) + postJson <- NewStyle.function.tryons(s"$InvalidJsonFormat The Json body should be the $CallLimitPostJsonV400 ", 400, callContext) { + json.extract[CallLimitPostJsonV400] + } + _ <- NewStyle.function.getConsumerByConsumerId(consumerId, callContext) + rateLimiting <- RateLimitingDI.rateLimiting.vend.updateConsumerCallLimits( + rateLimitingId, + postJson.from_date, + postJson.to_date, + postJson.api_version, + postJson.api_name, + postJson.bank_id, + Some(postJson.per_second_call_limit), + Some(postJson.per_minute_call_limit), + Some(postJson.per_hour_call_limit), + Some(postJson.per_day_call_limit), + Some(postJson.per_week_call_limit), + Some(postJson.per_month_call_limit)) map { + unboxFullOrFail(_, callContext, UpdateConsumerError) + } + } yield { + (createCallsLimitJson(rateLimiting), HttpCode.`200`(callContext)) + } + } + } + + staticResourceDocs += ResourceDoc( deleteCallLimits, implementedInApiVersion, nameOf(deleteCallLimits), "DELETE", - "/management/consumers/CONSUMER_ID/consumer/call-limits/RATE_LIMITING_ID", - "Delete Call Limit by Rate Limiting ID", + "/management/consumers/CONSUMER_ID/consumer/rate-limits/RATE_LIMITING_ID", + "Delete Rate Limit by Rate Limiting ID", s""" - |Delete a specific Call Limit by Rate Limiting ID + |Delete a specific Rate Limit by Rate Limiting ID | |${userAuthenticationMessage(true)} | @@ -270,16 +341,16 @@ trait APIMethods600 { UnknownError ), List(apiTagConsumer), - Some(List(canDeleteRateLimiting))) + Some(List(canDeleteRateLimits))) lazy val deleteCallLimits: OBPEndpoint = { - case "management" :: "consumers" :: consumerId :: "consumer" :: "call-limits" :: rateLimitingId :: Nil JsonDelete _ => + case "management" :: "consumers" :: consumerId :: "consumer" :: "rate-limits" :: rateLimitingId :: Nil JsonDelete _ => cc => implicit val ec = EndpointContext(Some(cc)) for { (Full(u), callContext) <- authenticatedAccess(cc) - _ <- NewStyle.function.hasEntitlement("", u.userId, canDeleteRateLimiting, callContext) + _ <- NewStyle.function.hasEntitlement("", u.userId, canDeleteRateLimits, callContext) _ <- NewStyle.function.getConsumerByConsumerId(consumerId, callContext) rateLimiting <- RateLimitingDI.rateLimiting.vend.getByRateLimitingId(rateLimitingId) _ <- rateLimiting match { @@ -305,10 +376,10 @@ trait APIMethods600 { implementedInApiVersion, nameOf(getActiveCallLimitsAtDate), "GET", - "/management/consumers/CONSUMER_ID/consumer/call-limits/active-at-date/DATE", - "Get Active Call Limits at Date", + "/management/consumers/CONSUMER_ID/consumer/rate-limits/active-at-date/DATE", + "Get Active Rate Limits at Date", s""" - |Get the sum of call limits at a certain date time. This returns a SUM of all the records that span that time. + |Get the sum of rate limits at a certain date time. This returns a SUM of all the records that span that time. | |Date format: YYYY-MM-DDTHH:MM:SSZ (e.g. 1099-12-31T23:00:00Z) | @@ -330,7 +401,7 @@ trait APIMethods600 { lazy val getActiveCallLimitsAtDate: OBPEndpoint = { - case "management" :: "consumers" :: consumerId :: "consumer" :: "call-limits" :: "active-at-date" :: dateString :: Nil JsonGet _ => + case "management" :: "consumers" :: consumerId :: "consumer" :: "rate-limits" :: "active-at-date" :: dateString :: Nil JsonGet _ => cc => implicit val ec = EndpointContext(Some(cc)) for { @@ -520,13 +591,14 @@ trait APIMethods600 { |The user creating this will be automatically assigned the Role CanCreateEntitlementAtOneBank. |Thus the User can manage the bank they create and assign Roles to other Users. | - |Only SANDBOX mode - |The settlement accounts are created specified by the bank in the POST body. + Only SANDBOX mode (i.e. when connector=mapped in properties file) + |The settlement accounts are automatically created by the system when the bank is created. |Name and account id are created in accordance to the next rules: | - Incoming account (name: Default incoming settlement account, Account ID: OBP_DEFAULT_INCOMING_ACCOUNT_ID, currency: EUR) | - Outgoing account (name: Default outgoing settlement account, Account ID: OBP_DEFAULT_OUTGOING_ACCOUNT_ID, currency: EUR) | |""", + postBankJson600, bankJson500, List( @@ -617,7 +689,7 @@ trait APIMethods600 { |DirectLogin is a simple authentication flow. You POST your credentials (username, password, and consumer key) |to the DirectLogin endpoint and receive a token in return. | - |This is an alias to the legacy DirectLogin endpoint that includes the standard API versioning prefix. + |This is an alias to the DirectLogin endpoint that includes the standard API versioning prefix. | |This endpoint requires the following headers: |- DirectLogin: username=YOUR_USERNAME, password=YOUR_PASSWORD, consumer_key=YOUR_CONSUMER_KEY diff --git a/obp-api/src/main/scala/code/ratelimiting/MappedRateLimiting.scala b/obp-api/src/main/scala/code/ratelimiting/MappedRateLimiting.scala index 03f46f6182..844e38f5d4 100644 --- a/obp-api/src/main/scala/code/ratelimiting/MappedRateLimiting.scala +++ b/obp-api/src/main/scala/code/ratelimiting/MappedRateLimiting.scala @@ -123,6 +123,44 @@ object MappedRateLimitingProvider extends RateLimitingProviderTrait { ).headOption } + def createConsumerCallLimits(consumerId: String, + fromDate: Date, + toDate: Date, + apiVersion: Option[String], + apiName: Option[String], + bankId: Option[String], + perSecond: Option[String], + perMinute: Option[String], + perHour: Option[String], + perDay: Option[String], + perWeek: Option[String], + perMonth: Option[String]): Future[Box[RateLimiting]] = Future { + + def createRateLimit(c: RateLimiting): Box[RateLimiting] = { + tryo { + c.FromDate(fromDate) + c.ToDate(toDate) + + perSecond.foreach(v => c.PerSecondCallLimit(v.toLong)) + perMinute.foreach(v => c.PerMinuteCallLimit(v.toLong)) + perHour.foreach(v => c.PerHourCallLimit(v.toLong)) + perDay.foreach(v => c.PerDayCallLimit(v.toLong)) + perWeek.foreach(v => c.PerWeekCallLimit(v.toLong)) + perMonth.foreach(v => c.PerMonthCallLimit(v.toLong)) + + c.BankId(bankId.orNull) + c.ApiName(apiName.orNull) + c.ApiVersion(apiVersion.orNull) + c.ConsumerId(consumerId) + + c.updatedAt(new Date()) + + c.saveMe() + } + } + val result = createRateLimit(RateLimiting.create) + result + } def createOrUpdateConsumerCallLimits(consumerId: String, fromDate: Date, toDate: Date, @@ -153,7 +191,6 @@ object MappedRateLimitingProvider extends RateLimitingProviderTrait { c.ApiVersion(apiVersion.orNull) c.ConsumerId(consumerId) - // 👇 bump timestamp for last-write-wins c.updatedAt(new Date()) c.saveMe() @@ -167,6 +204,40 @@ object MappedRateLimitingProvider extends RateLimitingProviderTrait { result } + def updateConsumerCallLimits(rateLimitingId: String, + fromDate: Date, + toDate: Date, + apiVersion: Option[String], + apiName: Option[String], + bankId: Option[String], + perSecond: Option[String], + perMinute: Option[String], + perHour: Option[String], + perDay: Option[String], + perWeek: Option[String], + perMonth: Option[String]): Future[Box[RateLimiting]] = Future { + RateLimiting.find( + By(RateLimiting.RateLimitingId, rateLimitingId) + ) map { c => + c.FromDate(fromDate) + c.ToDate(toDate) + + perSecond.foreach(v => c.PerSecondCallLimit(v.toLong)) + perMinute.foreach(v => c.PerMinuteCallLimit(v.toLong)) + perHour.foreach(v => c.PerHourCallLimit(v.toLong)) + perDay.foreach(v => c.PerDayCallLimit(v.toLong)) + perWeek.foreach(v => c.PerWeekCallLimit(v.toLong)) + perMonth.foreach(v => c.PerMonthCallLimit(v.toLong)) + + c.BankId(bankId.orNull) + c.ApiName(apiName.orNull) + c.ApiVersion(apiVersion.orNull) + + c.updatedAt(new Date()) + + c.saveMe() + } + } def deleteByRateLimitingId(rateLimitingId: String): Future[Box[Boolean]] = Future { tryo { diff --git a/obp-api/src/main/scala/code/ratelimiting/RateLimiting.scala b/obp-api/src/main/scala/code/ratelimiting/RateLimiting.scala index 5dfbf0ba27..f27b106ea6 100644 --- a/obp-api/src/main/scala/code/ratelimiting/RateLimiting.scala +++ b/obp-api/src/main/scala/code/ratelimiting/RateLimiting.scala @@ -30,6 +30,30 @@ trait RateLimitingProviderTrait { perDay: Option[String], perWeek: Option[String], perMonth: Option[String]): Future[Box[RateLimiting]] + def updateConsumerCallLimits(rateLimitingId: String, + fromDate: Date, + toDate: Date, + apiVersion: Option[String], + apiName: Option[String], + bankId: Option[String], + perSecond: Option[String], + perMinute: Option[String], + perHour: Option[String], + perDay: Option[String], + perWeek: Option[String], + perMonth: Option[String]): Future[Box[RateLimiting]] + def createConsumerCallLimits(consumerId: String, + fromDate: Date, + toDate: Date, + apiVersion: Option[String], + apiName: Option[String], + bankId: Option[String], + perSecond: Option[String], + perMinute: Option[String], + perHour: Option[String], + perDay: Option[String], + perWeek: Option[String], + perMonth: Option[String]): Future[Box[RateLimiting]] def deleteByRateLimitingId(rateLimitingId: String): Future[Box[Boolean]] def getByRateLimitingId(rateLimitingId: String): Future[Box[RateLimiting]] def getActiveCallLimitsByConsumerIdAtDate(consumerId: String, date: Date): Future[List[RateLimiting]] diff --git a/obp-api/src/main/scala/code/util/SecureLogging.scala b/obp-api/src/main/scala/code/util/SecureLogging.scala index 98ec9bfe0f..1c3b28b0f4 100644 --- a/obp-api/src/main/scala/code/util/SecureLogging.scala +++ b/obp-api/src/main/scala/code/util/SecureLogging.scala @@ -37,6 +37,9 @@ object SecureLogging { conditionalPattern("securelogging_mask_client_secret") { (Pattern.compile("(?i)(client_secret[\"']?\\s*[:=]\\s*[\"']?)([^\"',\\s&]+)"), "$1***") }, + conditionalPattern("securelogging_mask_client_secret") { + (Pattern.compile("(?i)(client_secret\\s*->\\s*)([^,\\s&\\)]+)"), "$1***") + }, // Authorization / Tokens conditionalPattern("securelogging_mask_authorization") { @@ -45,31 +48,55 @@ object SecureLogging { conditionalPattern("securelogging_mask_access_token") { (Pattern.compile("(?i)(access_token[\"']?\\s*[:=]\\s*[\"']?)([^\"',\\s&]+)"), "$1***") }, + conditionalPattern("securelogging_mask_access_token") { + (Pattern.compile("(?i)(access_token\\s*->\\s*)([^,\\s&\\)]+)"), "$1***") + }, conditionalPattern("securelogging_mask_refresh_token") { (Pattern.compile("(?i)(refresh_token[\"']?\\s*[:=]\\s*[\"']?)([^\"',\\s&]+)"), "$1***") }, + conditionalPattern("securelogging_mask_refresh_token") { + (Pattern.compile("(?i)(refresh_token\\s*->\\s*)([^,\\s&\\)]+)"), "$1***") + }, conditionalPattern("securelogging_mask_id_token") { (Pattern.compile("(?i)(id_token[\"']?\\s*[:=]\\s*[\"']?)([^\"',\\s&]+)"), "$1***") }, + conditionalPattern("securelogging_mask_id_token") { + (Pattern.compile("(?i)(id_token\\s*->\\s*)([^,\\s&\\)]+)"), "$1***") + }, conditionalPattern("securelogging_mask_token") { (Pattern.compile("(?i)(token[\"']?\\s*[:=]\\s*[\"']?)([^\"',\\s&]+)"), "$1***") }, + conditionalPattern("securelogging_mask_token") { + (Pattern.compile("(?i)(token\\s*->\\s*)([^,\\s&\\)]+)"), "$1***") + }, // Passwords conditionalPattern("securelogging_mask_password") { (Pattern.compile("(?i)(password[\"']?\\s*[:=]\\s*[\"']?)([^\"',\\s&]+)"), "$1***") }, + conditionalPattern("securelogging_mask_password") { + (Pattern.compile("(?i)(password\\s*->\\s*)([^,\\s&\\)]+)"), "$1***") + }, // API keys conditionalPattern("securelogging_mask_api_key") { (Pattern.compile("(?i)(api_key[\"']?\\s*[:=]\\s*[\"']?)([^\"',\\s&]+)"), "$1***") }, + conditionalPattern("securelogging_mask_api_key") { + (Pattern.compile("(?i)(api_key\\s*->\\s*)([^,\\s&\\)]+)"), "$1***") + }, conditionalPattern("securelogging_mask_key") { (Pattern.compile("(?i)(key[\"']?\\s*[:=]\\s*[\"']?)([^\"',\\s&]+)"), "$1***") }, + conditionalPattern("securelogging_mask_key") { + (Pattern.compile("(?i)(key\\s*->\\s*)([^,\\s&\\)]+)"), "$1***") + }, conditionalPattern("securelogging_mask_private_key") { (Pattern.compile("(?i)(private_key[\"']?\\s*[:=]\\s*[\"']?)([^\"',\\s&]+)"), "$1***") }, + conditionalPattern("securelogging_mask_private_key") { + (Pattern.compile("(?i)(private_key\\s*->\\s*)([^,\\s&\\)]+)"), "$1***") + }, // Database conditionalPattern("securelogging_mask_jdbc") { @@ -158,7 +185,14 @@ object SecureLogging { "api_key=sk_test_1234567890abcdef", "Error connecting to jdbc:mysql://localhost:3306/obp?user=admin:secretpassword@dbhost", "Credit card: 4532-1234-5678-9012 was processed", - "User email: sensitive@example.com in auth context" + "User email: sensitive@example.com in auth context", + "Map(client_secret -> my_client_secret, token -> secret_token)", + "Map(client_secret->my_client_secret, access_token->oauth_token_123)", + "directLoginParams=Map(password -> secret123, api_key -> sk_live_key)", + "client_secret -> my_client_secret", + "client_secret->my_client_secret", + "CallContext(oAuthParams=Map(access_token -> bearer_token, client_secret->sensitive_key))", + "Map(token->private_token, password -> supersecret, api_key->sk_live_123)" ) testMessages.map(msg => (msg, maskSensitive(msg))) } diff --git a/obp-api/src/test/scala/code/api/v3_1_0/RateLimitTest.scala b/obp-api/src/test/scala/code/api/v3_1_0/RateLimitTest.scala index 612d95f866..6aeca45e0e 100644 --- a/obp-api/src/test/scala/code/api/v3_1_0/RateLimitTest.scala +++ b/obp-api/src/test/scala/code/api/v3_1_0/RateLimitTest.scala @@ -32,7 +32,7 @@ import java.time.{ZoneId, ZonedDateTime} import java.util.Date import code.api.util.APIUtil.OAuth._ import code.api.util.{ApiRole, RateLimitingUtil} -import code.api.util.ApiRole.{CanReadCallLimits, CanSetCallLimits} +import code.api.util.ApiRole.{CanReadCallLimits, CanUpdateRateLimits} import code.api.util.ErrorMessages.{UserHasMissingRoles, UserNotLoggedIn} import code.api.v3_1_0.OBPAPI3_1_0.Implementations3_1_0 import code.consumer.Consumers @@ -155,22 +155,22 @@ class RateLimitTest extends V310ServerSetup with PropsReset { And("error should be " + UserNotLoggedIn) response310.body.extract[ErrorMessage].message should equal (UserNotLoggedIn) } - scenario("We will try to set calls limit per minute without a proper Role " + ApiRole.canSetCallLimits, ApiEndpoint, VersionOfApi) { - When("We make a request v3.1.0 without a Role " + ApiRole.canSetCallLimits) + scenario("We will try to set calls limit per minute without a proper Role " + ApiRole.canUpdateRateLimits, ApiEndpoint, VersionOfApi) { + When("We make a request v3.1.0 without a Role " + ApiRole.canUpdateRateLimits) val Some((c, _)) = user1 val consumerId = Consumers.consumers.vend.getConsumerByConsumerKey(c.key).map(_.consumerId.get).getOrElse("") val request310 = (v3_1_0_Request / "management" / "consumers" / consumerId / "consumer" / "call-limits").PUT <@(user1) val response310 = makePutRequest(request310, write(callLimitJson1)) Then("We should get a 403") response310.code should equal(403) - And("error should be " + UserHasMissingRoles + CanSetCallLimits) - response310.body.extract[ErrorMessage].message should equal (UserHasMissingRoles + CanSetCallLimits) + And("error should be " + UserHasMissingRoles + CanUpdateRateLimits) + response310.body.extract[ErrorMessage].message should equal (UserHasMissingRoles + CanUpdateRateLimits) } - scenario("We will try to set calls limit per minute with a proper Role " + ApiRole.canSetCallLimits, ApiEndpoint, VersionOfApi) { - When("We make a request v3.1.0 with a Role " + ApiRole.canSetCallLimits) + scenario("We will try to set calls limit per minute with a proper Role " + ApiRole.canUpdateRateLimits, ApiEndpoint, VersionOfApi) { + When("We make a request v3.1.0 with a Role " + ApiRole.canUpdateRateLimits) val Some((c, _)) = user1 val consumerId = Consumers.consumers.vend.getConsumerByConsumerKey(c.key).map(_.consumerId.get).getOrElse("") - Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanSetCallLimits.toString) + Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanUpdateRateLimits.toString) val request310 = (v3_1_0_Request / "management" / "consumers" / consumerId / "consumer" / "call-limits").PUT <@(user1) val response310 = makePutRequest(request310, write(callLimitJson1)) Then("We should get a 200") @@ -179,11 +179,11 @@ class RateLimitTest extends V310ServerSetup with PropsReset { } scenario("We will set calls limit per second for a Consumer", ApiEndpoint, VersionOfApi) { - When("We make a request v3.1.0 with a Role " + ApiRole.canSetCallLimits) + When("We make a request v3.1.0 with a Role " + ApiRole.canUpdateRateLimits) val Some((c, _)) = user1 val consumerId = Consumers.consumers.vend.getConsumerByConsumerKey(c.key).map(_.consumerId.get).getOrElse("") val id: Long = Consumers.consumers.vend.getConsumerByConsumerKey(c.key).map(_.id.get).getOrElse(0) - Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanSetCallLimits.toString) + Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanUpdateRateLimits.toString) val request310 = (v3_1_0_Request / "management" / "consumers" / consumerId / "consumer" / "call-limits").PUT <@(user1) val response01 = makePutRequest(request310, write(callLimitSecondJson)) Then("We should get a 200") @@ -204,11 +204,11 @@ class RateLimitTest extends V310ServerSetup with PropsReset { } scenario("We will set calls limit per minute for a Consumer", ApiEndpoint, VersionOfApi) { - When("We make a request v3.1.0 with a Role " + ApiRole.canSetCallLimits) + When("We make a request v3.1.0 with a Role " + ApiRole.canUpdateRateLimits) val Some((c, _)) = user1 val consumerId = Consumers.consumers.vend.getConsumerByConsumerKey(c.key).map(_.consumerId.get).getOrElse("") val id: Long = Consumers.consumers.vend.getConsumerByConsumerKey(c.key).map(_.id.get).getOrElse(0) - Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanSetCallLimits.toString) + Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanUpdateRateLimits.toString) val request310 = (v3_1_0_Request / "management" / "consumers" / consumerId / "consumer" / "call-limits").PUT <@(user1) val response01 = makePutRequest(request310, write(callLimitMinuteJson)) Then("We should get a 200") @@ -229,11 +229,11 @@ class RateLimitTest extends V310ServerSetup with PropsReset { } scenario("We will set calls limit per hour for a Consumer", ApiEndpoint, VersionOfApi) { - When("We make a request v3.1.0 with a Role " + ApiRole.canSetCallLimits) + When("We make a request v3.1.0 with a Role " + ApiRole.canUpdateRateLimits) val Some((c, _)) = user1 val consumerId = Consumers.consumers.vend.getConsumerByConsumerKey(c.key).map(_.consumerId.get).getOrElse("") val id: Long = Consumers.consumers.vend.getConsumerByConsumerKey(c.key).map(_.id.get).getOrElse(0) - Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanSetCallLimits.toString) + Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanUpdateRateLimits.toString) val request310 = (v3_1_0_Request / "management" / "consumers" / consumerId / "consumer" / "call-limits").PUT <@(user1) val response01 = makePutRequest(request310, write(callLimitHourJson)) Then("We should get a 200") @@ -254,11 +254,11 @@ class RateLimitTest extends V310ServerSetup with PropsReset { } scenario("We will set calls limit per day for a Consumer", ApiEndpoint, VersionOfApi) { - When("We make a request v3.1.0 with a Role " + ApiRole.canSetCallLimits) + When("We make a request v3.1.0 with a Role " + ApiRole.canUpdateRateLimits) val Some((c, _)) = user1 val consumerId = Consumers.consumers.vend.getConsumerByConsumerKey(c.key).map(_.consumerId.get).getOrElse("") val id: Long = Consumers.consumers.vend.getConsumerByConsumerKey(c.key).map(_.id.get).getOrElse(0) - Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanSetCallLimits.toString) + Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanUpdateRateLimits.toString) val request310 = (v3_1_0_Request / "management" / "consumers" / consumerId / "consumer" / "call-limits").PUT <@(user1) val response01 = makePutRequest(request310, write(callLimitDayJson)) Then("We should get a 200") @@ -279,11 +279,11 @@ class RateLimitTest extends V310ServerSetup with PropsReset { } scenario("We will set calls limit per week for a Consumer", ApiEndpoint, VersionOfApi) { - When("We make a request v3.1.0 with a Role " + ApiRole.canSetCallLimits) + When("We make a request v3.1.0 with a Role " + ApiRole.canUpdateRateLimits) val Some((c, _)) = user1 val consumerId = Consumers.consumers.vend.getConsumerByConsumerKey(c.key).map(_.consumerId.get).getOrElse("") val id: Long = Consumers.consumers.vend.getConsumerByConsumerKey(c.key).map(_.id.get).getOrElse(0) - Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanSetCallLimits.toString) + Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanUpdateRateLimits.toString) val request310 = (v3_1_0_Request / "management" / "consumers" / consumerId / "consumer" / "call-limits").PUT <@(user1) val response01 = makePutRequest(request310, write(callLimitWeekJson)) Then("We should get a 200") @@ -304,11 +304,11 @@ class RateLimitTest extends V310ServerSetup with PropsReset { } scenario("We will set calls limit per month for a Consumer", ApiEndpoint, VersionOfApi) { - When("We make a request v3.1.0 with a Role " + ApiRole.canSetCallLimits) + When("We make a request v3.1.0 with a Role " + ApiRole.canUpdateRateLimits) val Some((c, _)) = user1 val consumerId = Consumers.consumers.vend.getConsumerByConsumerKey(c.key).map(_.consumerId.get).getOrElse("") val id: Long = Consumers.consumers.vend.getConsumerByConsumerKey(c.key).map(_.id.get).getOrElse(0) - Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanSetCallLimits.toString) + Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanUpdateRateLimits.toString) val request310 = (v3_1_0_Request / "management" / "consumers" / consumerId / "consumer" / "call-limits").PUT <@(user1) val response01 = makePutRequest(request310, write(callLimitMonthJson)) Then("We should get a 200") 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 36a93050c6..d0a76181c5 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 @@ -27,7 +27,7 @@ package code.api.v4_0_0 import code.api.cache.Redis import code.api.util.APIUtil.OAuth._ -import code.api.util.ApiRole.{CanSetCallLimits, canCreateDynamicEndpoint} +import code.api.util.ApiRole.{CanUpdateRateLimits, canCreateDynamicEndpoint} import code.api.util.ErrorMessages.{UserHasMissingRoles, UserNotLoggedIn} import code.api.util.{ApiRole, ExampleValue, RateLimitingUtil} import code.api.v3_0_0.OBPAPI3_0_0.Implementations3_0_0.getCurrentUser @@ -101,18 +101,18 @@ class RateLimitingTest extends V400ServerSetup with PropsReset { And("error should be " + UserNotLoggedIn) 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) { + scenario("We will try to set Rate Limiting per minute without a proper Role " + ApiRole.canUpdateRateLimits, ApiCallsLimit, ApiVersion400) { - When("We make a request v4.0.0 without a Role " + ApiRole.canSetCallLimits) + When("We make a request v4.0.0 without a Role " + ApiRole.canUpdateRateLimits) val response400 = setRateLimitingWithoutRole(user1, callLimitJsonInitial) Then("We should get a 403") response400.code should equal(403) - And("error should be " + UserHasMissingRoles + CanSetCallLimits) - response400.body.extract[ErrorMessage].message should equal (UserHasMissingRoles + CanSetCallLimits) + And("error should be " + UserHasMissingRoles + CanUpdateRateLimits) + response400.body.extract[ErrorMessage].message should equal (UserHasMissingRoles + CanUpdateRateLimits) } - scenario("We will try to set Rate Limiting per minute with a proper Role " + ApiRole.canSetCallLimits, ApiCallsLimit, ApiVersion400) { + scenario("We will try to set Rate Limiting per minute with a proper Role " + ApiRole.canUpdateRateLimits, ApiCallsLimit, ApiVersion400) { - When("We make a request v4.0.0 with a Role " + ApiRole.canSetCallLimits) + When("We make a request v4.0.0 with a Role " + ApiRole.canUpdateRateLimits) val response400 = setRateLimiting(user1, callLimitJsonInitial) Then("We should get a 200") response400.code should equal(200) @@ -120,7 +120,7 @@ class RateLimitingTest extends V400ServerSetup with PropsReset { } 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) + When("We make a request v4.0.0 with a Role " + ApiRole.canUpdateRateLimits) val response01 = setRateLimiting(user1, callLimitJsonSecond) Then("We should get a 200") response01.code should equal(200) @@ -137,13 +137,13 @@ class RateLimitingTest extends V400ServerSetup with PropsReset { response03.code should equal(429) // Revert to initial state - val response04 = setRateLimiting(user1, callLimitJsonInitial) + val response04 = setRateLimiting2(user1, callLimitJsonInitial) Then("We should get a 200") 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) + When("We make a request v4.0.0 with a Role " + ApiRole.canUpdateRateLimits) val response01 = setRateLimiting(user1, callLimitJsonMinute) Then("We should get a 200") response01.code should equal(200) @@ -159,13 +159,13 @@ class RateLimitingTest extends V400ServerSetup with PropsReset { response03.code should equal(429) // Revert to initial state - val response04 = setRateLimiting(user1, callLimitJsonInitial) + val response04 = setRateLimiting2(user1, callLimitJsonInitial) Then("We should get a 200") 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) + When("We make a request v4.0.0 with a Role " + ApiRole.canUpdateRateLimits) val response01 = setRateLimiting(user1, callLimitJsonHour) Then("We should get a 200") response01.code should equal(200) @@ -181,13 +181,13 @@ class RateLimitingTest extends V400ServerSetup with PropsReset { response03.code should equal(429) // Revert to initial state - val response04 = setRateLimiting(user1, callLimitJsonInitial) + val response04 = setRateLimiting2(user1, callLimitJsonInitial) Then("We should get a 200") 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) + When("We make a request v4.0.0 with a Role " + ApiRole.canUpdateRateLimits) val response01 = setRateLimiting(user1, callLimitJsonWeek) Then("We should get a 200") response01.code should equal(200) @@ -203,13 +203,13 @@ class RateLimitingTest extends V400ServerSetup with PropsReset { response03.code should equal(429) // Revert to initial state - val response04 = setRateLimiting(user1, callLimitJsonInitial) + val response04 = setRateLimiting2(user1, callLimitJsonInitial) Then("We should get a 200") 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) + When("We make a request v4.0.0 with a Role " + ApiRole.canUpdateRateLimits) val response01 = setRateLimiting(user1, callLimitJsonMonth) Then("We should get a 200") response01.code should equal(200) @@ -225,7 +225,7 @@ class RateLimitingTest extends V400ServerSetup with PropsReset { response03.code should equal(429) // Revert to initial state - val response04 = setRateLimiting(user1, callLimitJsonInitial) + val response04 = setRateLimiting2(user1, callLimitJsonInitial) Then("We should get a 200") response04.code should equal(200) } @@ -248,7 +248,7 @@ class RateLimitingTest extends V400ServerSetup with PropsReset { val operationId = "dynamicEndpoint_GET_accounts_ACCOUNT_ID" val apiName = "dynamicEndpoint_GET_accounts_ACCOUNT_ID" val apiVersion = ApiVersion.`dynamic-endpoint`.toString() - val response01 = setRateLimiting(user1, callLimitJsonHour.copy(api_name = Some(apiName), api_version = Some(apiVersion))) + val response01 = setRateLimiting2(user1, callLimitJsonHour.copy(api_name = Some(apiName), api_version = Some(apiVersion))) Then("We should get a 200") response01.code should equal(200) @@ -263,7 +263,7 @@ class RateLimitingTest extends V400ServerSetup with PropsReset { makeGetRequest(requestDynamicEndpoint.GET <@(user1)).code should equal(429) // Revert Rate Limiting to initial state in case of a Dynamic Endpoint - val response02 = setRateLimiting(user1, callLimitJsonInitial.copy(api_name = Some(apiName), api_version = Some(apiVersion))) + val response02 = setRateLimiting2(user1, callLimitJsonInitial.copy(api_name = Some(apiName), api_version = Some(apiVersion))) Then("We should get a 200") response02.code should equal(200) diff --git a/obp-api/src/test/scala/code/api/v4_0_0/V400ServerSetup.scala b/obp-api/src/test/scala/code/api/v4_0_0/V400ServerSetup.scala index 86d5ec1117..dbd09abf61 100644 --- a/obp-api/src/test/scala/code/api/v4_0_0/V400ServerSetup.scala +++ b/obp-api/src/test/scala/code/api/v4_0_0/V400ServerSetup.scala @@ -104,10 +104,17 @@ trait V400ServerSetup extends ServerSetupWithTestData with DefaultUsers { def setRateLimiting(consumerAndToken: Option[(Consumer, Token)], putJson: CallLimitPostJsonV400): APIResponse = { val Some((c, _)) = consumerAndToken val consumerId = Consumers.consumers.vend.getConsumerByConsumerKey(c.key).map(_.consumerId.get).getOrElse("") - Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanSetCallLimits.toString) + Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanUpdateRateLimits.toString) val request400 = (v4_0_0_Request / "management" / "consumers" / consumerId / "consumer" / "call-limits").PUT <@(consumerAndToken) makePutRequest(request400, write(putJson)) - } + } + def setRateLimiting2(consumerAndToken: Option[(Consumer, Token)], putJson: CallLimitPostJsonV400): APIResponse = { + val Some((c, _)) = consumerAndToken + val consumerId = Consumers.consumers.vend.getConsumerByConsumerKey(c.key).map(_.consumerId.get).getOrElse("") + Entitlement.entitlement.vend.addEntitlement("", resourceUser2.userId, ApiRole.CanUpdateRateLimits.toString) + val request400 = (v4_0_0_Request / "management" / "consumers" / consumerId / "consumer" / "call-limits").PUT <@ user2 + makePutRequest(request400, write(putJson)) + } def setRateLimitingWithoutRole(consumerAndToken: Option[(Consumer, Token)], putJson: CallLimitPostJsonV400): APIResponse = { val Some((c, _)) = consumerAndToken val consumerId = Consumers.consumers.vend.getConsumerByConsumerKey(c.key).map(_.consumerId.get).getOrElse("") diff --git a/obp-api/src/test/scala/code/api/v5_1_0/RateLimitingTest.scala b/obp-api/src/test/scala/code/api/v5_1_0/RateLimitingTest.scala index 9991aeba54..bda8c01781 100644 --- a/obp-api/src/test/scala/code/api/v5_1_0/RateLimitingTest.scala +++ b/obp-api/src/test/scala/code/api/v5_1_0/RateLimitingTest.scala @@ -91,7 +91,7 @@ class RateLimitingTest extends V510ServerSetup with PropsReset { When(s"We make a request $ApiVersion510") val Some((c, _)) = user1 val consumerId = Consumers.consumers.vend.getConsumerByConsumerKey(c.key).map(_.consumerId.get).getOrElse("") - val request510 = (v5_1_0_Request / "management" / "consumers" / consumerId / "consumer" / "call-limits").GET + val request510 = (v5_1_0_Request / "management" / "consumers" / consumerId / "consumer" / "rate-limits").GET val response510 = makeGetRequest(request510) Then("We should get a 401") response510.code should equal(401) @@ -102,7 +102,7 @@ class RateLimitingTest extends V510ServerSetup with PropsReset { When("We make a request v3.1.0 without a Role " + ApiRole.canReadCallLimits) val Some((c, _)) = user1 val consumerId = Consumers.consumers.vend.getConsumerByConsumerKey(c.key).map(_.consumerId.get).getOrElse("") - val request510 = (v5_1_0_Request / "management" / "consumers" / consumerId / "consumer" / "call-limits").GET <@ (user1) + val request510 = (v5_1_0_Request / "management" / "consumers" / consumerId / "consumer" / "rate-limits").GET <@ (user1) val response510 = makeGetRequest(request510) Then("We should get a 403") response510.code should equal(403) @@ -111,7 +111,7 @@ class RateLimitingTest extends V510ServerSetup with PropsReset { } scenario("We will try to get calls limit per minute with a proper Role " + ApiRole.canReadCallLimits, ApiCallsLimit, ApiVersion510) { - When("We make a request v5.1.0 with a Role " + ApiRole.canSetCallLimits) + When("We make a request v5.1.0 with a Role " + ApiRole.canUpdateRateLimits) val response01 = setRateLimiting(user1, callLimitJsonMonth) Then("We should get a 200") response01.code should equal(200) @@ -120,7 +120,7 @@ class RateLimitingTest extends V510ServerSetup with PropsReset { val Some((c, _)) = user1 val consumerId = Consumers.consumers.vend.getConsumerByConsumerKey(c.key).map(_.consumerId.get).getOrElse("") Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanReadCallLimits.toString) - val request510 = (v5_1_0_Request / "management" / "consumers" / consumerId / "consumer" / "call-limits").GET <@ (user1) + val request510 = (v5_1_0_Request / "management" / "consumers" / consumerId / "consumer" / "rate-limits").GET <@ (user1) val response510 = makeGetRequest(request510) Then("We should get a 200") response510.code should equal(200) diff --git a/obp-api/src/test/scala/code/api/v5_1_0/V510ServerSetup.scala b/obp-api/src/test/scala/code/api/v5_1_0/V510ServerSetup.scala index 32fa520658..cbea88008e 100644 --- a/obp-api/src/test/scala/code/api/v5_1_0/V510ServerSetup.scala +++ b/obp-api/src/test/scala/code/api/v5_1_0/V510ServerSetup.scala @@ -37,7 +37,7 @@ trait V510ServerSetup extends ServerSetupWithTestData with DefaultUsers { def setRateLimiting(consumerAndToken: Option[(Consumer, Token)], putJson: CallLimitPostJsonV400): APIResponse = { val Some((c, _)) = consumerAndToken val consumerId = Consumers.consumers.vend.getConsumerByConsumerKey(c.key).map(_.consumerId.get).getOrElse("") - Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanSetCallLimits.toString) + Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanUpdateRateLimits.toString) val request400 = (v4_0_0_Request / "management" / "consumers" / consumerId / "consumer" / "call-limits").PUT <@ (consumerAndToken) makePutRequest(request400, write(putJson)) } diff --git a/obp-api/src/test/scala/code/api/v6_0_0/CallLimitsTest.scala b/obp-api/src/test/scala/code/api/v6_0_0/CallLimitsTest.scala index 915d4b2b9d..f8869e8817 100644 --- a/obp-api/src/test/scala/code/api/v6_0_0/CallLimitsTest.scala +++ b/obp-api/src/test/scala/code/api/v6_0_0/CallLimitsTest.scala @@ -26,7 +26,7 @@ TESOBE (http://www.tesobe.com/) package code.api.v6_0_0 import code.api.util.APIUtil.OAuth._ -import code.api.util.ApiRole.{CanDeleteRateLimiting, CanReadCallLimits, CanSetCallLimits} +import code.api.util.ApiRole.{CanDeleteRateLimiting, CanReadCallLimits, CanCreateRateLimits} import code.api.util.ErrorMessages.{UserHasMissingRoles, UserNotLoggedIn} import code.api.v6_0_0.OBPAPI6_0_0.Implementations6_0_0 import code.consumer.Consumers @@ -46,6 +46,7 @@ class CallLimitsTest extends V600ServerSetup { object VersionOfApi extends Tag(ApiVersion.v6_0_0.toString) object ApiEndpoint1 extends Tag(nameOf(Implementations6_0_0.createCallLimits)) object ApiEndpoint2 extends Tag(nameOf(Implementations6_0_0.deleteCallLimits)) + object UpdateRateLimits extends Tag(nameOf(Implementations6_0_0.updateRateLimits)) object ApiEndpoint3 extends Tag(nameOf(Implementations6_0_0.getActiveCallLimitsAtDate)) lazy val postCallLimitJsonV600 = CallLimitPostJsonV600( @@ -75,7 +76,7 @@ class CallLimitsTest extends V600ServerSetup { When("We make a request v6.0.0 without user credentials") val Some((c, _)) = user1 val consumerId = Consumers.consumers.vend.getConsumerByConsumerKey(c.key).map(_.consumerId.get).getOrElse("") - val request600 = (v6_0_0_Request / "management" / "consumers" / consumerId / "consumer" / "call-limits").POST + val request600 = (v6_0_0_Request / "management" / "consumers" / consumerId / "consumer" / "rate-limits").POST val response600 = makePostRequest(request600, write(postCallLimitJsonV600)) Then("We should get a 401") response600.code should equal(401) @@ -89,20 +90,20 @@ class CallLimitsTest extends V600ServerSetup { When("We make a request v6.0.0 without a proper role") val Some((c, _)) = user1 val consumerId = Consumers.consumers.vend.getConsumerByConsumerKey(c.key).map(_.consumerId.get).getOrElse("") - val request600 = (v6_0_0_Request / "management" / "consumers" / consumerId / "consumer" / "call-limits").POST <@ (user1) + val request600 = (v6_0_0_Request / "management" / "consumers" / consumerId / "consumer" / "rate-limits").POST <@ (user1) val response600 = makePostRequest(request600, write(postCallLimitJsonV600)) Then("We should get a 403") response600.code should equal(403) - And("error should be " + UserHasMissingRoles + CanSetCallLimits) - response600.body.extract[ErrorMessage].message should equal(UserHasMissingRoles + CanSetCallLimits) + And("error should be " + UserHasMissingRoles + CanCreateRateLimits) + response600.body.extract[ErrorMessage].message should equal(UserHasMissingRoles + CanCreateRateLimits) } scenario("We will call the endpoint with proper Role", ApiEndpoint1, VersionOfApi) { When("We make a request v6.0.0 with a proper role") val Some((c, _)) = user1 val consumerId = Consumers.consumers.vend.getConsumerByConsumerKey(c.key).map(_.consumerId.get).getOrElse("") - Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanSetCallLimits.toString) - val request600 = (v6_0_0_Request / "management" / "consumers" / consumerId / "consumer" / "call-limits").POST <@ (user1) + Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanCreateRateLimits.toString) + val request600 = (v6_0_0_Request / "management" / "consumers" / consumerId / "consumer" / "rate-limits").POST <@ (user1) val response600 = makePostRequest(request600, write(postCallLimitJsonV600)) Then("We should get a 201") response600.code should equal(201) @@ -119,15 +120,15 @@ class CallLimitsTest extends V600ServerSetup { Given("We create a call limit first") val Some((c, _)) = user1 val consumerId = Consumers.consumers.vend.getConsumerByConsumerKey(c.key).map(_.consumerId.get).getOrElse("") - Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanSetCallLimits.toString) - val request600 = (v6_0_0_Request / "management" / "consumers" / consumerId / "consumer" / "call-limits").POST <@ (user1) + Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanCreateRateLimits.toString) + val request600 = (v6_0_0_Request / "management" / "consumers" / consumerId / "consumer" / "rate-limits").POST <@ (user1) val createResponse = makePostRequest(request600, write(postCallLimitJsonV600)) createResponse.code should equal(201) val createdCallLimit = createResponse.body.extract[CallLimitJsonV600] When("We delete the call limit") Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanDeleteRateLimiting.toString) - val deleteRequest = (v6_0_0_Request / "management" / "consumers" / consumerId / "consumer" / "call-limits" / createdCallLimit.rate_limiting_id).DELETE <@ (user1) + val deleteRequest = (v6_0_0_Request / "management" / "consumers" / consumerId / "consumer" / "rate-limits" / createdCallLimit.rate_limiting_id).DELETE <@ (user1) val deleteResponse = makeDeleteRequest(deleteRequest) Then("We should get a 204") @@ -138,14 +139,14 @@ class CallLimitsTest extends V600ServerSetup { Given("We create a call limit first") val Some((c, _)) = user1 val consumerId = Consumers.consumers.vend.getConsumerByConsumerKey(c.key).map(_.consumerId.get).getOrElse("") - Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanSetCallLimits.toString) - val request600 = (v6_0_0_Request / "management" / "consumers" / consumerId / "consumer" / "call-limits").POST <@ (user1) + Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanCreateRateLimits.toString) + val request600 = (v6_0_0_Request / "management" / "consumers" / consumerId / "consumer" / "rate-limits").POST <@ (user1) val createResponse = makePostRequest(request600, write(postCallLimitJsonV600)) createResponse.code should equal(201) val createdCallLimit = createResponse.body.extract[CallLimitJsonV600] When("We try to delete without proper role") - val deleteRequest = (v6_0_0_Request / "management" / "consumers" / consumerId / "consumer" / "call-limits" / createdCallLimit.rate_limiting_id).DELETE <@ (user1) + val deleteRequest = (v6_0_0_Request / "management" / "consumers" / consumerId / "consumer" / "rate-limits" / createdCallLimit.rate_limiting_id).DELETE <@ (user1) val deleteResponse = makeDeleteRequest(deleteRequest) Then("We should get a 403") @@ -160,8 +161,8 @@ class CallLimitsTest extends V600ServerSetup { Given("We create a call limit first") val Some((c, _)) = user1 val consumerId = Consumers.consumers.vend.getConsumerByConsumerKey(c.key).map(_.consumerId.get).getOrElse("") - Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanSetCallLimits.toString) - val request600 = (v6_0_0_Request / "management" / "consumers" / consumerId / "consumer" / "call-limits").POST <@ (user1) + Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanCreateRateLimits.toString) + val request600 = (v6_0_0_Request / "management" / "consumers" / consumerId / "consumer" / "rate-limits").POST <@ (user1) val createResponse = makePostRequest(request600, write(postCallLimitJsonV600)) createResponse.code should equal(201) @@ -170,7 +171,7 @@ class CallLimitsTest extends V600ServerSetup { val currentDateString = ZonedDateTime .now(ZoneOffset.UTC) .format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'")) - val getRequest = (v6_0_0_Request / "management" / "consumers" / consumerId / "consumer" / "call-limits" / "active-at-date" / currentDateString).GET <@ (user1) + val getRequest = (v6_0_0_Request / "management" / "consumers" / consumerId / "consumer" / "rate-limits" / "active-at-date" / currentDateString).GET <@ (user1) val getResponse = makeGetRequest(getRequest) Then("We should get a 200") @@ -188,7 +189,7 @@ class CallLimitsTest extends V600ServerSetup { val currentDateString = ZonedDateTime .now(ZoneOffset.UTC) .format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'")) - val getRequest = (v6_0_0_Request / "management" / "consumers" / consumerId / "consumer" / "call-limits" / "active-at-date" / currentDateString).GET <@ (user1) + val getRequest = (v6_0_0_Request / "management" / "consumers" / consumerId / "consumer" / "rate-limits" / "active-at-date" / currentDateString).GET <@ (user1) val getResponse = makeGetRequest(getRequest) Then("We should get a 403") diff --git a/obp-api/src/test/scala/code/util/ApiSessionTest.scala b/obp-api/src/test/scala/code/util/ApiSessionTest.scala index c0d2c0a1ed..b8eb4564d1 100644 --- a/obp-api/src/test/scala/code/util/ApiSessionTest.scala +++ b/obp-api/src/test/scala/code/util/ApiSessionTest.scala @@ -56,4 +56,30 @@ class ApiSessionTest extends FeatureSpec with Matchers with GivenWhenThen with M callContextUpdated.get.sessionId should be (Some("12345")) } } + + feature("test CallContext toString secure logging masking") + { + scenario("toString should mask sensitive data") + { + val callContextWithSensitiveData = CallContext( + directLoginParams = Map("password" -> "supersecret"), + oAuthParams = Map("client_secret" -> "my_client_secret") + ) + + val toStringResult = callContextWithSensitiveData.toString + + // Verify that sensitive data is masked - should NOT contain the actual sensitive values + toStringResult should not contain "supersecret" + toStringResult should not contain "my_client_secret" + + // Verify that the result contains the case class structure (not just object reference) + toStringResult should include("CallContext") + + // Verify that masking occurs by checking for masked patterns or lack of sensitive data + val containsActualSensitiveData = toStringResult.contains("supersecret") || + toStringResult.contains("my_client_secret") + + containsActualSensitiveData should be (false) + } + } } \ No newline at end of file diff --git a/obp-commons/pom.xml b/obp-commons/pom.xml index b41909faf1..eac3d3709e 100644 --- a/obp-commons/pom.xml +++ b/obp-commons/pom.xml @@ -91,30 +91,7 @@ - - org.owasp - dependency-check-maven - 7.1.1 - - notifier-dependency-check - HTML - 10 - false - true - - true - true - false - pom - - - - - aggregate - - - - + org.apache.maven.plugins maven-surefire-plugin @@ -178,4 +155,61 @@ + + + + dependency-check + + + + env.NVD_API_KEY + + + + + + + org.owasp + dependency-check-maven + 8.4.3 + + + ${env.NVD_API_KEY} + true + false + true + + notifier-dependency-check + HTML + ${project.build.directory}/dependency-check-report + 10 + false + true + true + true + pom + + + false + false + false + false + false + false + + + + + dependency-check + verify + + check + + + + + + + +