diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index 0e122ae429..43d049926b 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -23,7 +23,6 @@ import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.support.DefaultMessageSourceResolvable; @@ -47,6 +46,7 @@ import org.thingsboard.server.common.data.EntityViewInfo; import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.common.data.OtaPackageInfo; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.Tenant; @@ -120,6 +120,7 @@ import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.rpc.RpcService; import org.thingsboard.server.dao.rule.RuleChainService; +import org.thingsboard.server.dao.service.Validator; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantProfileService; import org.thingsboard.server.dao.tenant.TenantService; @@ -131,7 +132,6 @@ import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.provider.TbQueueProducerProvider; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.component.ComponentDiscoveryService; -import org.thingsboard.server.service.edge.EdgeNotificationService; import org.thingsboard.server.service.edge.rpc.EdgeRpcService; import org.thingsboard.server.service.entitiy.TbNotificationEntityService; import org.thingsboard.server.service.ota.OtaPackageStateService; @@ -418,7 +418,7 @@ public abstract class BaseController { PageLink createPageLink(int pageSize, int page, String textSearch, String sortProperty, String sortOrder) throws ThingsboardException { if (StringUtils.isNotEmpty(sortProperty)) { - if (!StringUtils.isAlphanumeric(sortProperty)) { + if (!Validator.isValidProperty(sortProperty)) { throw new IllegalArgumentException("Invalid sort property"); } SortOrder.Direction direction = SortOrder.Direction.ASC; diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseEntityQueryControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseEntityQueryControllerTest.java index 1ffa5aa324..85d68a479d 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseEntityQueryControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseEntityQueryControllerTest.java @@ -400,7 +400,7 @@ public abstract class BaseEntityQueryControllerTest extends AbstractControllerTe EntityDataQuery query = new EntityDataQuery(filter, pageLink, entityFields, latestValues, null); ResultActions result = doPost("/api/entitiesQuery/find", query).andExpect(status().isBadRequest()); - assertThat(getErrorMessage(result)).contains("Invalid").contains("sort key"); + assertThat(getErrorMessage(result)).contains("Invalid").contains("sort property"); } } diff --git a/common/util/src/main/java/org/thingsboard/common/util/RegexUtils.java b/common/util/src/main/java/org/thingsboard/common/util/RegexUtils.java index af968a6bff..1f47b41a14 100644 --- a/common/util/src/main/java/org/thingsboard/common/util/RegexUtils.java +++ b/common/util/src/main/java/org/thingsboard/common/util/RegexUtils.java @@ -33,4 +33,8 @@ public class RegexUtils { }); } + public static boolean matches(String input, Pattern pattern) { + return pattern.matcher(input).matches(); + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java index e6012602f1..76637218dc 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java @@ -65,6 +65,7 @@ import java.util.concurrent.Executors; import java.util.stream.Collectors; import java.util.stream.Stream; +import static org.thingsboard.server.dao.service.Validator.validateEntityDataPageLink; import static org.thingsboard.server.dao.service.Validator.validateId; @Service @@ -139,6 +140,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ public PageData findAlarmDataByQueryForEntities(TenantId tenantId, AlarmDataQuery query, Collection orderedEntityIds) { validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateEntityDataPageLink(query.getPageLink()); return alarmDao.findAlarmDataByQueryForEntities(tenantId, query, orderedEntityIds); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java index a349e0a2c0..bb1c633f85 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java @@ -60,6 +60,7 @@ import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.user.UserService; import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; +import static org.thingsboard.server.dao.service.Validator.validateEntityDataPageLink; import static org.thingsboard.server.dao.service.Validator.validateId; /** @@ -239,21 +240,6 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe validateEntityDataPageLink(query.getPageLink()); } - private static void validateEntityDataPageLink(EntityDataPageLink pageLink) { - if (pageLink == null) { - throw new IncorrectParameterException("Entity Data Page link must be specified."); - } else if (pageLink.getPageSize() < 1) { - throw new IncorrectParameterException("Incorrect entity data page link page size '" + pageLink.getPageSize() + "'. Page size must be greater than zero."); - } else if (pageLink.getPage() < 0) { - throw new IncorrectParameterException("Incorrect entity data page link page '" + pageLink.getPage() + "'. Page must be positive integer."); - } else if (pageLink.getSortOrder() != null && pageLink.getSortOrder().getKey() != null) { - String sortKey = pageLink.getSortOrder().getKey().getKey(); - if (StringUtils.isNotEmpty(sortKey) && !StringUtils.isAlphanumeric(sortKey)) { - throw new IncorrectParameterException("Invalid entity data page link sort key"); - } - } - } - private static void validateRelationQuery(RelationsQueryFilter queryFilter) { if (queryFilter.isMultiRoot() && queryFilter.getMultiRootEntitiesType() ==null){ throw new IncorrectParameterException("Multi-root relation query filter should contain 'multiRootEntitiesType'"); diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java b/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java index c5c6040deb..21aaf0c653 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java @@ -15,16 +15,24 @@ */ package org.thingsboard.server.dao.service; +import org.apache.commons.lang3.StringUtils; +import org.thingsboard.common.util.RegexUtils; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.UUIDBased; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.query.EntityDataPageLink; +import org.thingsboard.server.common.data.query.EntityKey; +import org.thingsboard.server.common.data.query.EntityKeyType; import org.thingsboard.server.dao.exception.IncorrectParameterException; import java.util.List; import java.util.UUID; +import java.util.regex.Pattern; public class Validator { + public static final Pattern PROPERTY_PATTERN = Pattern.compile("^[\\p{L}0-9_-]+$"); // Unicode letters, numbers, '_' and '-' allowed + /** * This method validate EntityId entity id. If entity id is invalid than throw * IncorrectParameterException exception @@ -122,7 +130,31 @@ public class Validator { throw new IncorrectParameterException("Incorrect page link page size '"+pageLink.getPageSize()+"'. Page size must be greater than zero."); } else if (pageLink.getPage() < 0) { throw new IncorrectParameterException("Incorrect page link page '"+pageLink.getPage()+"'. Page must be positive integer."); + } else if (pageLink.getSortOrder() != null) { + if (!isValidProperty(pageLink.getSortOrder().getProperty())) { + throw new IncorrectParameterException("Invalid page link sort property"); + } } } + public static void validateEntityDataPageLink(EntityDataPageLink pageLink) { + if (pageLink == null) { + throw new IncorrectParameterException("Entity Data Page link must be specified."); + } else if (pageLink.getPageSize() < 1) { + throw new IncorrectParameterException("Incorrect entity data page link page size '" + pageLink.getPageSize() + "'. Page size must be greater than zero."); + } else if (pageLink.getPage() < 0) { + throw new IncorrectParameterException("Incorrect entity data page link page '" + pageLink.getPage() + "'. Page must be positive integer."); + } else if (pageLink.getSortOrder() != null && pageLink.getSortOrder().getKey() != null) { + EntityKey sortKey = pageLink.getSortOrder().getKey(); + if ((sortKey.getType() == EntityKeyType.ENTITY_FIELD || sortKey.getType() == EntityKeyType.ALARM_FIELD) + && !isValidProperty(sortKey.getKey())) { + throw new IncorrectParameterException("Invalid entity data page link sort property"); + } + } + } + + public static boolean isValidProperty(String key) { + return StringUtils.isEmpty(key) || RegexUtils.matches(key, PROPERTY_PATTERN); + } + }