thingsboard-aplcache

Details

diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml
index da35c6c..a2ae7e2 100644
--- a/application/src/main/resources/thingsboard.yml
+++ b/application/src/main/resources/thingsboard.yml
@@ -110,11 +110,20 @@ coap:
 #Quota parameters
 quota:
   host:
+    # Max allowed number of API requests in interval for single host
     limit: "${QUOTA_HOST_LIMIT:10000}"
+    # Interval duration
     intervalMs: "${QUOTA_HOST_INTERVAL_MS:60000}"
+    # Maximum silence duration for host after which Host removed from QuotaService. Must be bigger than intervalMs
     ttlMs: "${QUOTA_HOST_TTL_MS:60000}"
+    # Interval for scheduled task that cleans expired records. TTL is used for expiring
     cleanPeriodMs: "${QUOTA_HOST_CLEAN_PERIOD_MS:300000}"
+    # Enable Host API Limits
     enabled: "${QUOTA_HOST_ENABLED:false}"
+    # Array of whitelist hosts
+    whitelist: "${QUOTA_HOST_WHITELIST:localhost,127.0.0.1}"
+    # Array of blacklist hosts
+    blacklist: "${QUOTA_HOST_BLACKLIST:}"
   log:
     topSize: 10
     intervalMin: 2
diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/HostRequestIntervalRegistry.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/HostRequestIntervalRegistry.java
index 9f8f946..4db6ff0 100644
--- a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/HostRequestIntervalRegistry.java
+++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/HostRequestIntervalRegistry.java
@@ -1,12 +1,12 @@
 /**
  * Copyright © 2016-2017 The Thingsboard Authors
- *
+ * <p>
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -15,12 +15,15 @@
  */
 package org.thingsboard.server.common.transport.quota.inmemory;
 
+import com.google.common.collect.Sets;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Component;
 
 import javax.annotation.PostConstruct;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.stream.Collectors;
 
@@ -35,11 +38,17 @@ public class HostRequestIntervalRegistry {
     private final Map<String, IntervalCount> hostCounts = new ConcurrentHashMap<>();
     private final long intervalDurationMs;
     private final long ttlMs;
+    private final Set<String> whiteList;
+    private final Set<String> blackList;
 
     public HostRequestIntervalRegistry(@Value("${quota.host.intervalMs}") long intervalDurationMs,
-                                       @Value("${quota.host.ttlMs}") long ttlMs) {
+                                       @Value("${quota.host.ttlMs}") long ttlMs,
+                                       @Value("${quota.host.whitelist}") String whiteList,
+                                       @Value("${quota.host.blacklist}") String blackList) {
         this.intervalDurationMs = intervalDurationMs;
         this.ttlMs = ttlMs;
+        this.whiteList = Sets.newHashSet(StringUtils.split(whiteList, ','));
+        this.blackList = Sets.newHashSet(StringUtils.split(blackList, ','));
     }
 
     @PostConstruct
@@ -47,11 +56,15 @@ public class HostRequestIntervalRegistry {
         if (ttlMs < intervalDurationMs) {
             log.warn("TTL for IntervalRegistry [{}] smaller than interval duration [{}]", ttlMs, intervalDurationMs);
         }
+        log.info("Start Host Quota Service with whitelist {}", whiteList);
+        log.info("Start Host Quota Service with blacklist {}", blackList);
     }
 
     public long tick(String clientHostId) {
-        if ("localhost".equals(clientHostId) || "127.0.0.1".equals(clientHostId)) {
+        if (whiteList.contains(clientHostId)) {
             return 0;
+        } else if (blackList.contains(clientHostId)) {
+            return Long.MAX_VALUE;
         }
         IntervalCount intervalCount = hostCounts.computeIfAbsent(clientHostId, s -> new IntervalCount(intervalDurationMs));
         return intervalCount.resetIfExpiredAndTick();
diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryLogger.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryLogger.java
index 91797d4..3f660fc 100644
--- a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryLogger.java
+++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryLogger.java
@@ -80,7 +80,7 @@ public class IntervalRegistryLogger {
     private void log(Map<String, Long> top) {
         StringBuilder builder = new StringBuilder("Quota Statistic : ");
         for (Map.Entry<String, Long> host : top.entrySet()) {
-            builder.append(host.getKey()).append(" : ").append(host.getValue());
+            builder.append(host.getKey()).append(" : ").append(host.getValue()).append(" ; ");
         }
 
         log.info(builder.toString());
diff --git a/common/transport/src/test/java/org/thingsboard/server/common/transport/quota/HostRequestsQuotaServiceTest.java b/common/transport/src/test/java/org/thingsboard/server/common/transport/quota/HostRequestsQuotaServiceTest.java
index 475fccc..b8c9284 100644
--- a/common/transport/src/test/java/org/thingsboard/server/common/transport/quota/HostRequestsQuotaServiceTest.java
+++ b/common/transport/src/test/java/org/thingsboard/server/common/transport/quota/HostRequestsQuotaServiceTest.java
@@ -44,9 +44,9 @@ public class HostRequestsQuotaServiceTest {
     }
 
     @Test
-    public void hostQuotaValidated() {
+    public void quotaExceededIfRequestCountBiggerThanAllowed() {
         when(requestRegistry.tick("key")).thenReturn(10L);
-        when(requestsPolicy.isValid(10L)).thenReturn(true);
+        when(requestsPolicy.isValid(10L)).thenReturn(false);
 
         assertTrue(quotaService.isQuotaExceeded("key"));
 
@@ -56,6 +56,18 @@ public class HostRequestsQuotaServiceTest {
     }
 
     @Test
+    public void quotaNotExceededIfRequestCountLessThanAllowed() {
+        when(requestRegistry.tick("key")).thenReturn(10L);
+        when(requestsPolicy.isValid(10L)).thenReturn(true);
+
+        assertFalse(quotaService.isQuotaExceeded("key"));
+
+        verify(requestRegistry).tick("key");
+        verify(requestsPolicy).isValid(10L);
+        verifyNoMoreInteractions(requestRegistry, requestsPolicy);
+    }
+
+    @Test
     public void serviceCanBeDisabled() {
         quotaService = new HostRequestsQuotaService(requestRegistry, requestsPolicy, registryCleaner, registryLogger, false);
         assertFalse(quotaService.isQuotaExceeded("key"));
diff --git a/common/transport/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/HostRequestIntervalRegistryTest.java b/common/transport/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/HostRequestIntervalRegistryTest.java
index cb21cce..ff1e525 100644
--- a/common/transport/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/HostRequestIntervalRegistryTest.java
+++ b/common/transport/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/HostRequestIntervalRegistryTest.java
@@ -15,9 +15,12 @@
  */
 package org.thingsboard.server.common.transport.quota.inmemory;
 
+import com.google.common.collect.Sets;
 import org.junit.Before;
 import org.junit.Test;
 
+import java.util.Collections;
+
 import static org.junit.Assert.assertEquals;
 
 /**
@@ -30,7 +33,7 @@ public class HostRequestIntervalRegistryTest {
 
     @Before
     public void init() {
-        registry = new HostRequestIntervalRegistry(10000L, 100L);
+        registry = new HostRequestIntervalRegistry(10000L, 100L,"g1,g2", "b1");
     }
 
     @Test
@@ -54,4 +57,29 @@ public class HostRequestIntervalRegistryTest {
         assertEquals(1L, registry.tick("aaa"));
         assertEquals(2L, registry.tick("bbb"));
     }
+
+    @Test
+    public void domainFromWhitelistNotCounted(){
+        assertEquals(0L, registry.tick("g1"));
+        assertEquals(0L, registry.tick("g1"));
+        assertEquals(0L, registry.tick("g2"));
+    }
+
+    @Test
+    public void domainFromBlackListReturnMaxValue(){
+        assertEquals(Long.MAX_VALUE, registry.tick("b1"));
+        assertEquals(Long.MAX_VALUE, registry.tick("b1"));
+    }
+
+    @Test
+    public void emptyWhitelistParsedOk(){
+        registry = new HostRequestIntervalRegistry(10000L, 100L,"", "b1");
+        assertEquals(1L, registry.tick("aaa"));
+    }
+
+    @Test
+    public void emptyBlacklistParsedOk(){
+        registry = new HostRequestIntervalRegistry(10000L, 100L,"", "");
+        assertEquals(1L, registry.tick("aaa"));
+    }
 }
\ No newline at end of file
diff --git a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java b/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java
index 2ffbf05..958c4a7 100644
--- a/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java
+++ b/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java
@@ -75,7 +75,6 @@ public class CoapTransportResource extends CoapResource {
     @Override
     public void handleGET(CoapExchange exchange) {
         if(quotaService.isQuotaExceeded(exchange.getSourceAddress().getHostAddress())) {
-            log.warn("Missing feature type parameter");
             log.warn("COAP Quota exceeded for [{}:{}] . Disconnect", exchange.getSourceAddress().getHostAddress(), exchange.getSourcePort());
             exchange.respond(ResponseCode.BAD_REQUEST);
             return;