diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/queue/Queue.java b/common/data/src/main/java/org/thingsboard/server/common/data/queue/Queue.java index b4d65706a2..a6623be8a4 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/queue/Queue.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/queue/Queue.java @@ -22,11 +22,17 @@ import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; import org.thingsboard.server.common.data.id.QueueId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.tenant.profile.TenantProfileQueueConfiguration; +import org.thingsboard.server.common.data.validation.Length; +import org.thingsboard.server.common.data.validation.NoXss; @Data public class Queue extends SearchTextBasedWithAdditionalInfo implements HasName, HasTenantId { private TenantId tenantId; + @NoXss + @Length(fieldName = "name") private String name; + @NoXss + @Length(fieldName = "topic") private String topic; private int pollInterval; private int partitions; diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java index 1b27a2751f..81297b3594 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java @@ -17,6 +17,7 @@ package org.thingsboard.server.dao.service; import com.fasterxml.jackson.databind.JsonNode; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.TenantId; @@ -36,6 +37,11 @@ public abstract class DataValidator> { private static final Pattern EMAIL_PATTERN = Pattern.compile("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}$", Pattern.CASE_INSENSITIVE); + private static final Pattern QUEUE_PATTERN = Pattern.compile("^[a-zA-Z0-9_.\\-]+$"); + + private static final String NAME = "name"; + private static final String TOPIC = "topic"; + // Returns old instance of the same object that is fetched during validation. public D validate(D data, Function tenantIdFunction) { try { @@ -134,4 +140,22 @@ public abstract class DataValidator> { } } + protected static void validateQueueName(String name) { + validateQueueNameOrTopic(name, NAME); + } + + protected static void validateQueueTopic(String topic) { + validateQueueNameOrTopic(topic, TOPIC); + } + + private static void validateQueueNameOrTopic(String value, String fieldName) { + if (StringUtils.isEmpty(value)) { + throw new DataValidationException(String.format("Queue %s should be specified!", fieldName)); + } + if (!QUEUE_PATTERN.matcher(value).matches()) { + throw new DataValidationException( + String.format("Queue %s contains a character other than ASCII alphanumerics, '.', '_' and '-'!", fieldName)); + } + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/QueueValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/QueueValidator.java index c8ce639d40..1eaa64f446 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/QueueValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/QueueValidator.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.dao.service.validator; -import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.TenantProfile; @@ -73,12 +72,9 @@ public class QueueValidator extends DataValidator { } } - if (StringUtils.isEmpty(queue.getName())) { - throw new DataValidationException("Queue name should be specified!"); - } - if (StringUtils.isBlank(queue.getTopic())) { - throw new DataValidationException("Queue topic should be non empty and without spaces!"); - } + validateQueueName(queue.getName()); + validateQueueTopic(queue.getTopic()); + if (queue.getPollInterval() < 1) { throw new DataValidationException("Queue poll interval should be more then 0!"); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/TenantProfileDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/TenantProfileDataValidator.java index ce15ec9e88..feab5078e6 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/TenantProfileDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/TenantProfileDataValidator.java @@ -107,12 +107,9 @@ public class TenantProfileDataValidator extends DataValidator { } private void validateQueueConfiguration(TenantProfileQueueConfiguration queue) { - if (StringUtils.isEmpty(queue.getName())) { - throw new DataValidationException("Queue name should be specified!"); - } - if (StringUtils.isBlank(queue.getTopic())) { - throw new DataValidationException("Queue topic should be non empty and without spaces!"); - } + validateQueueName(queue.getName()); + validateQueueTopic(queue.getTopic()); + if (queue.getPollInterval() < 1) { throw new DataValidationException("Queue poll interval should be more then 0!"); } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseQueueServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseQueueServiceTest.java index 577193ff94..428a21299d 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseQueueServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseQueueServiceTest.java @@ -152,6 +152,20 @@ public abstract class BaseQueueServiceTest extends AbstractServiceTest { queueService.saveQueue(queue); } + @Test(expected = DataValidationException.class) + public void testSaveQueueWithInvalidName() { + Queue queue = new Queue(); + queue.setTenantId(tenantId); + queue.setName("Test 1"); + queue.setTopic("tb_rule_engine.test"); + queue.setPollInterval(25); + queue.setPartitions(1); + queue.setPackProcessingTimeout(2000); + queue.setSubmitStrategy(createTestSubmitStrategy()); + queue.setProcessingStrategy(createTestProcessingStrategy()); + queueService.saveQueue(queue); + } + @Test(expected = DataValidationException.class) public void testSaveQueueWithEmptyTopic() { Queue queue = new Queue(); @@ -165,6 +179,20 @@ public abstract class BaseQueueServiceTest extends AbstractServiceTest { queueService.saveQueue(queue); } + @Test(expected = DataValidationException.class) + public void testSaveQueueWithInvalidTopic() { + Queue queue = new Queue(); + queue.setTenantId(tenantId); + queue.setName("Test"); + queue.setTopic("tb rule engine test"); + queue.setPollInterval(25); + queue.setPartitions(1); + queue.setPackProcessingTimeout(2000); + queue.setSubmitStrategy(createTestSubmitStrategy()); + queue.setProcessingStrategy(createTestProcessingStrategy()); + queueService.saveQueue(queue); + } + @Test(expected = DataValidationException.class) public void testSaveQueueWithEmptyPollInterval() { Queue queue = new Queue(); diff --git a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.html b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.html index 6c34e0111f..6e291e9e23 100644 --- a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.html +++ b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.html @@ -26,6 +26,9 @@ {{ 'queue.name-unique' | translate }} + + {{ 'queue.name-pattern' | translate }} + diff --git a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts index b84dfc9985..30df2ff7ae 100644 --- a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts +++ b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts @@ -99,7 +99,7 @@ export class QueueFormComponent implements ControlValueAccessor, OnInit, OnDestr ngOnInit() { this.queueFormGroup = this.fb.group( { - name: ['', [Validators.required]], + name: ['', [Validators.required, Validators.pattern(/^[a-zA-Z0-9_.\-]+$/)]], pollInterval: [25, [Validators.min(1), Validators.required]], partitions: [10, [Validators.min(1), Validators.required]], consumerPerPartition: [false, []], diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 0286b5f9d2..747a12c336 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2928,6 +2928,7 @@ "name": "Name", "name-required": "Queue name is required!", "name-unique": "Queue name is not unique!", + "name-pattern": "Queue name contains a character other than ASCII alphanumerics, '.', '_' and '-'!", "queue-required": "Queue is required!", "topic-required": "Queue topic is required!", "poll-interval-required": "Poll interval is required!",