From 68f0388723f135972b764c9c62e7ea2512370425 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Tue, 28 Jun 2022 14:54:38 +0200 Subject: [PATCH 1/3] added queue name and topic validation --- .../server/common/data/queue/Queue.java | 6 ++++++ .../dao/service/validator/QueueValidator.java | 14 ++++++++++++-- .../validator/TenantProfileDataValidator.java | 13 +++++++++++-- .../components/queue/queue-form.component.html | 3 +++ .../home/components/queue/queue-form.component.ts | 2 +- .../src/assets/locale/locale.constant-en_US.json | 1 + 6 files changed, 34 insertions(+), 5 deletions(-) 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/validator/QueueValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/QueueValidator.java index c8ce639d40..a1d0df43d5 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 @@ -29,6 +29,8 @@ import org.thingsboard.server.dao.queue.QueueDao; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; +import java.util.regex.Pattern; + @Component public class QueueValidator extends DataValidator { @@ -38,6 +40,8 @@ public class QueueValidator extends DataValidator { @Autowired private TbTenantProfileCache tenantProfileCache; + private final Pattern queueTopicPattern = Pattern.compile("^[a-zA-Z0-9_.\\-]+$"); + @Override protected void validateCreate(TenantId tenantId, Queue queue) { if (queueDao.findQueueByTenantIdAndName(tenantId, queue.getName()) != null) { @@ -76,8 +80,14 @@ 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!"); + if (!queueTopicPattern.matcher(queue.getName()).matches()) { + throw new DataValidationException("Queue name contains a character other than ASCII alphanumerics, '.', '_' and '-'!"); + } + if (StringUtils.isEmpty(queue.getTopic())) { + throw new DataValidationException("Queue topic should be specified!"); + } + if (!queueTopicPattern.matcher(queue.getTopic()).matches()) { + throw new DataValidationException("Queue topic contains a character other than ASCII alphanumerics, '.', '_' and '-'!"); } 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..bfca262abf 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 @@ -34,6 +34,7 @@ import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.regex.Pattern; @Component public class TenantProfileDataValidator extends DataValidator { @@ -45,6 +46,8 @@ public class TenantProfileDataValidator extends DataValidator { @Lazy private TenantProfileService tenantProfileService; + private final Pattern queueTopicPattern = Pattern.compile("^[a-zA-Z0-9_.\\-]+$"); + @Override protected void validateDataImpl(TenantId tenantId, TenantProfile tenantProfile) { if (StringUtils.isEmpty(tenantProfile.getName())) { @@ -110,8 +113,14 @@ public class TenantProfileDataValidator 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!"); + if (!queueTopicPattern.matcher(queue.getName()).matches()) { + throw new DataValidationException("Queue name contains a character other than ASCII alphanumerics, '.', '_' and '-'!"); + } + if (StringUtils.isEmpty(queue.getTopic())) { + throw new DataValidationException("Queue topic should be specified!"); + } + if (!queueTopicPattern.matcher(queue.getTopic()).matches()) { + throw new DataValidationException("Queue topic contains a character other than ASCII alphanumerics, '.', '_' and '-'!"); } if (queue.getPollInterval() < 1) { throw new DataValidationException("Queue poll interval should be more then 0!"); 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!", From c21a9c116493bf532e5c6bb4616219e1b01c644d Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Tue, 28 Jun 2022 15:58:15 +0200 Subject: [PATCH 2/3] added queue validation tests --- .../dao/service/BaseQueueServiceTest.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) 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(); From ac3b133ec0dfdf9c08eac76bb685db4748d18ffb Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Wed, 29 Jun 2022 10:39:33 +0200 Subject: [PATCH 3/3] refactored queue validator --- .../server/dao/service/DataValidator.java | 24 +++++++++++++++++++ .../dao/service/validator/QueueValidator.java | 20 +++------------- .../validator/TenantProfileDataValidator.java | 18 +++----------- 3 files changed, 30 insertions(+), 32 deletions(-) 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 a1d0df43d5..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; @@ -29,8 +28,6 @@ import org.thingsboard.server.dao.queue.QueueDao; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; -import java.util.regex.Pattern; - @Component public class QueueValidator extends DataValidator { @@ -40,8 +37,6 @@ public class QueueValidator extends DataValidator { @Autowired private TbTenantProfileCache tenantProfileCache; - private final Pattern queueTopicPattern = Pattern.compile("^[a-zA-Z0-9_.\\-]+$"); - @Override protected void validateCreate(TenantId tenantId, Queue queue) { if (queueDao.findQueueByTenantIdAndName(tenantId, queue.getName()) != null) { @@ -77,18 +72,9 @@ public class QueueValidator extends DataValidator { } } - if (StringUtils.isEmpty(queue.getName())) { - throw new DataValidationException("Queue name should be specified!"); - } - if (!queueTopicPattern.matcher(queue.getName()).matches()) { - throw new DataValidationException("Queue name contains a character other than ASCII alphanumerics, '.', '_' and '-'!"); - } - if (StringUtils.isEmpty(queue.getTopic())) { - throw new DataValidationException("Queue topic should be specified!"); - } - if (!queueTopicPattern.matcher(queue.getTopic()).matches()) { - throw new DataValidationException("Queue topic contains a character other than ASCII alphanumerics, '.', '_' and '-'!"); - } + 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 bfca262abf..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 @@ -34,7 +34,6 @@ import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; -import java.util.regex.Pattern; @Component public class TenantProfileDataValidator extends DataValidator { @@ -46,8 +45,6 @@ public class TenantProfileDataValidator extends DataValidator { @Lazy private TenantProfileService tenantProfileService; - private final Pattern queueTopicPattern = Pattern.compile("^[a-zA-Z0-9_.\\-]+$"); - @Override protected void validateDataImpl(TenantId tenantId, TenantProfile tenantProfile) { if (StringUtils.isEmpty(tenantProfile.getName())) { @@ -110,18 +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 (!queueTopicPattern.matcher(queue.getName()).matches()) { - throw new DataValidationException("Queue name contains a character other than ASCII alphanumerics, '.', '_' and '-'!"); - } - if (StringUtils.isEmpty(queue.getTopic())) { - throw new DataValidationException("Queue topic should be specified!"); - } - if (!queueTopicPattern.matcher(queue.getTopic()).matches()) { - throw new DataValidationException("Queue topic contains a character other than ASCII alphanumerics, '.', '_' and '-'!"); - } + validateQueueName(queue.getName()); + validateQueueTopic(queue.getTopic()); + if (queue.getPollInterval() < 1) { throw new DataValidationException("Queue poll interval should be more then 0!"); }