diff --git a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java index 4919c0019b..7a6a1620ae 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java @@ -76,6 +76,7 @@ import org.thingsboard.server.common.data.kv.StringDataEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.common.transport.adaptor.JsonConverter; +import org.thingsboard.server.dao.service.ConstraintValidator; import org.thingsboard.server.dao.timeseries.TimeseriesService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.AccessValidator; @@ -184,7 +185,11 @@ public class TelemetryController extends BaseController { public DeferredResult getAttributeKeys( @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, defaultValue = "DEVICE") @PathVariable("entityType") String entityType, @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr) throws ThingsboardException { - return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, this::getAttributeKeysCallback); + try { + return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, this::getAttributeKeysCallback); + } catch (Exception e) { + throw handleException(e); + } } @ApiOperation(value = "Get all attribute keys by scope (getAttributeKeysByScope)", @@ -201,8 +206,12 @@ public class TelemetryController extends BaseController { @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, defaultValue = "DEVICE") @PathVariable("entityType") String entityType, @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, required = true, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES) @PathVariable("scope") String scope) throws ThingsboardException { - return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, - (result, tenantId, entityId) -> getAttributeKeysCallback(result, tenantId, entityId, scope)); + try { + return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, + (result, tenantId, entityId) -> getAttributeKeysCallback(result, tenantId, entityId, scope)); + } catch (Exception e) { + throw handleException(e); + } } @ApiOperation(value = "Get attributes (getAttributes)", @@ -220,9 +229,13 @@ public class TelemetryController extends BaseController { @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, defaultValue = "DEVICE") @PathVariable("entityType") String entityType, @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @ApiParam(value = ATTRIBUTES_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException { - SecurityUser user = getCurrentUser(); - return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, - (result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, null, keysStr)); + try { + SecurityUser user = getCurrentUser(); + return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, + (result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, null, keysStr)); + } catch (Exception e) { + throw handleException(e); + } } @@ -244,9 +257,13 @@ public class TelemetryController extends BaseController { @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES, required = true) @PathVariable("scope") String scope, @ApiParam(value = ATTRIBUTES_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException { - SecurityUser user = getCurrentUser(); - return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, - (result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, scope, keysStr)); + try { + SecurityUser user = getCurrentUser(); + return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, + (result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, scope, keysStr)); + } catch (Exception e) { + throw handleException(e); + } } @ApiOperation(value = "Get time-series keys (getTimeseriesKeys)", @@ -259,8 +276,12 @@ public class TelemetryController extends BaseController { public DeferredResult getTimeseriesKeys( @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, defaultValue = "DEVICE") @PathVariable("entityType") String entityType, @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr) throws ThingsboardException { - return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, - (result, tenantId, entityId) -> Futures.addCallback(tsService.findAllLatest(tenantId, entityId), getTsKeysToResponseCallback(result), MoreExecutors.directExecutor())); + try { + return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, + (result, tenantId, entityId) -> Futures.addCallback(tsService.findAllLatest(tenantId, entityId), getTsKeysToResponseCallback(result), MoreExecutors.directExecutor())); + } catch (Exception e) { + throw handleException(e); + } } @ApiOperation(value = "Get latest time-series value (getLatestTimeseries)", @@ -285,9 +306,13 @@ public class TelemetryController extends BaseController { @ApiParam(value = TELEMETRY_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr, @ApiParam(value = STRICT_DATA_TYPES_DESCRIPTION) @RequestParam(name = "useStrictDataTypes", required = false, defaultValue = "false") Boolean useStrictDataTypes) throws ThingsboardException { - SecurityUser user = getCurrentUser(); - return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, - (result, tenantId, entityId) -> getLatestTimeseriesValuesCallback(result, user, entityId, keysStr, useStrictDataTypes)); + try { + SecurityUser user = getCurrentUser(); + return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, + (result, tenantId, entityId) -> getLatestTimeseriesValuesCallback(result, user, entityId, keysStr, useStrictDataTypes)); + } catch (Exception e) { + throw handleException(e); + } } @ApiOperation(value = "Get time-series data (getTimeseries)", @@ -324,15 +349,19 @@ public class TelemetryController extends BaseController { @RequestParam(name = "orderBy", defaultValue = "DESC") String orderBy, @ApiParam(value = STRICT_DATA_TYPES_DESCRIPTION) @RequestParam(name = "useStrictDataTypes", required = false, defaultValue = "false") Boolean useStrictDataTypes) throws ThingsboardException { - return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, - (result, tenantId, entityId) -> { - // If interval is 0, convert this to a NONE aggregation, which is probably what the user really wanted - Aggregation agg = interval == 0L ? Aggregation.valueOf(Aggregation.NONE.name()) : Aggregation.valueOf(aggStr); - List queries = toKeysList(keys).stream().map(key -> new BaseReadTsKvQuery(key, startTs, endTs, interval, limit, agg, orderBy)) - .collect(Collectors.toList()); - - Futures.addCallback(tsService.findAll(tenantId, entityId, queries), getTsKvListCallback(result, useStrictDataTypes), MoreExecutors.directExecutor()); - }); + try { + return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, + (result, tenantId, entityId) -> { + // If interval is 0, convert this to a NONE aggregation, which is probably what the user really wanted + Aggregation agg = interval == 0L ? Aggregation.valueOf(Aggregation.NONE.name()) : Aggregation.valueOf(aggStr); + List queries = toKeysList(keys).stream().map(key -> new BaseReadTsKvQuery(key, startTs, endTs, interval, limit, agg, orderBy)) + .collect(Collectors.toList()); + + Futures.addCallback(tsService.findAll(tenantId, entityId, queries), getTsKvListCallback(result, useStrictDataTypes), MoreExecutors.directExecutor()); + }); + } catch (Exception e) { + throw handleException(e); + } } @ApiOperation(value = "Save device attributes (saveDeviceAttributes)", @@ -356,8 +385,12 @@ public class TelemetryController extends BaseController { @ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION, required = true) @PathVariable("deviceId") String deviceIdStr, @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES, required = true) @PathVariable("scope") String scope, @ApiParam(value = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody JsonNode request) throws ThingsboardException { - EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr); - return saveAttributes(getTenantId(), entityId, scope, request); + try { + EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr); + return saveAttributes(getTenantId(), entityId, scope, request); + } catch (Exception e) { + throw handleException(e); + } } @ApiOperation(value = "Save entity attributes (saveEntityAttributesV1)", @@ -380,8 +413,12 @@ public class TelemetryController extends BaseController { @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES) @PathVariable("scope") String scope, @ApiParam(value = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody JsonNode request) throws ThingsboardException { - EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); - return saveAttributes(getTenantId(), entityId, scope, request); + try { + EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); + return saveAttributes(getTenantId(), entityId, scope, request); + } catch (Exception e) { + throw handleException(e); + } } @ApiOperation(value = "Save entity attributes (saveEntityAttributesV2)", @@ -404,8 +441,12 @@ public class TelemetryController extends BaseController { @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES, required = true) @PathVariable("scope") String scope, @ApiParam(value = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody JsonNode request) throws ThingsboardException { - EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); - return saveAttributes(getTenantId(), entityId, scope, request); + try { + EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); + return saveAttributes(getTenantId(), entityId, scope, request); + } catch (Exception e) { + throw handleException(e); + } } @@ -429,8 +470,12 @@ public class TelemetryController extends BaseController { @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @ApiParam(value = TELEMETRY_SCOPE_DESCRIPTION, required = true, allowableValues = "ANY") @PathVariable("scope") String scope, @ApiParam(value = TELEMETRY_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody String requestBody) throws ThingsboardException { - EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); - return saveTelemetry(getTenantId(), entityId, requestBody, 0L); + try { + EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); + return saveTelemetry(getTenantId(), entityId, requestBody, 0L); + } catch (Exception e) { + throw handleException(e); + } } @ApiOperation(value = "Save or update time-series data with TTL (saveEntityTelemetryWithTTL)", @@ -455,8 +500,12 @@ public class TelemetryController extends BaseController { @ApiParam(value = TELEMETRY_SCOPE_DESCRIPTION, required = true, allowableValues = "ANY") @PathVariable("scope") String scope, @ApiParam(value = "A long value representing TTL (Time to Live) parameter.", required = true) @PathVariable("ttl") Long ttl, @ApiParam(value = TELEMETRY_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody String requestBody) throws ThingsboardException { - EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); - return saveTelemetry(getTenantId(), entityId, requestBody, ttl); + try { + EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); + return saveTelemetry(getTenantId(), entityId, requestBody, ttl); + } catch (Exception e) { + throw handleException(e); + } } @ApiOperation(value = "Delete entity time-series data (deleteEntityTimeseries)", @@ -489,8 +538,12 @@ public class TelemetryController extends BaseController { @RequestParam(name = "endTs", required = false) Long endTs, @ApiParam(value = "If the parameter is set to true, the latest telemetry will be rewritten in case that current latest value was removed, otherwise, in case that parameter is set to false the new latest value will not set.") @RequestParam(name = "rewriteLatestIfDeleted", defaultValue = "false") boolean rewriteLatestIfDeleted) throws ThingsboardException { - EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); - return deleteTimeseries(entityId, keysStr, deleteAllDataForKeys, startTs, endTs, rewriteLatestIfDeleted); + try { + EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); + return deleteTimeseries(entityId, keysStr, deleteAllDataForKeys, startTs, endTs, rewriteLatestIfDeleted); + } catch (Exception e) { + throw handleException(e); + } } private DeferredResult deleteTimeseries(EntityId entityIdStr, String keysStr, boolean deleteAllDataForKeys, @@ -556,8 +609,12 @@ public class TelemetryController extends BaseController { @ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION, required = true) @PathVariable(DEVICE_ID) String deviceIdStr, @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES, required = true) @PathVariable("scope") String scope, @ApiParam(value = ATTRIBUTES_KEYS_DESCRIPTION, required = true) @RequestParam(name = "keys") String keysStr) throws ThingsboardException { - EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr); - return deleteAttributes(entityId, scope, keysStr); + try { + EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr); + return deleteAttributes(entityId, scope, keysStr); + } catch (Exception e) { + throw handleException(e); + } } @ApiOperation(value = "Delete entity attributes (deleteEntityAttributes)", @@ -580,8 +637,12 @@ public class TelemetryController extends BaseController { @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, required = true, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES) @PathVariable("scope") String scope, @ApiParam(value = ATTRIBUTES_KEYS_DESCRIPTION, required = true) @RequestParam(name = "keys") String keysStr) throws ThingsboardException { - EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); - return deleteAttributes(entityId, scope, keysStr); + try { + EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); + return deleteAttributes(entityId, scope, keysStr); + } catch (Exception e) { + throw handleException(e); + } } private DeferredResult deleteAttributes(EntityId entityIdSrc, String scope, String keysStr) throws ThingsboardException { @@ -627,6 +688,7 @@ public class TelemetryController extends BaseController { } if (json.isObject()) { List attributes = extractRequestAttributes(json); + attributes.forEach(ConstraintValidator::validateFields); if (attributes.isEmpty()) { return getImmediateDeferredResult("No attributes data found in request body!", HttpStatus.BAD_REQUEST); } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java index 244eb0e05d..c72b732ff6 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java @@ -99,6 +99,20 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { Assert.assertEquals(foundAsset.getName(), savedAsset.getName()); } + @Test + public void testSaveAssetWithViolationOfLengthValidation() throws Exception { + Asset asset = new Asset(); + asset.setName(RandomStringUtils.randomAlphabetic(300)); + asset.setType("default"); + doPost("/api/asset", asset).andExpect(statusReason(containsString("length of name must be equal or less than 255"))); + asset.setName("Normal name"); + asset.setType(RandomStringUtils.randomAlphabetic(300)); + doPost("/api/asset", asset).andExpect(statusReason(containsString("length of type must be equal or less than 255"))); + asset.setType("default"); + asset.setLabel(RandomStringUtils.randomAlphabetic(300)); + doPost("/api/asset", asset).andExpect(statusReason(containsString("length of label must be equal or less than 255"))); + } + @Test public void testUpdateAssetFromDifferentTenant() throws Exception { Asset asset = new Asset(); diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java index 4fbf08e595..318e1413a0 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java @@ -90,6 +90,28 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest .andExpect(status().isOk()); } + @Test + public void testSaveCustomerWithViolationOfValidation() throws Exception { + Customer customer = new Customer(); + customer.setTitle(RandomStringUtils.randomAlphabetic(300)); + doPost("/api/customer", customer).andExpect(statusReason(containsString("length of title must be equal or less than 255"))); + customer.setTitle("Normal title"); + customer.setCity(RandomStringUtils.randomAlphabetic(300)); + doPost("/api/customer", customer).andExpect(statusReason(containsString("length of city must be equal or less than 255"))); + customer.setCity("Normal city"); + customer.setCountry(RandomStringUtils.randomAlphabetic(300)); + doPost("/api/customer", customer).andExpect(statusReason(containsString("length of country must be equal or less than 255"))); + customer.setCountry("Ukraine"); + customer.setPhone(RandomStringUtils.randomAlphabetic(300)); + doPost("/api/customer", customer).andExpect(statusReason(containsString("length of phone must be equal or less than 255"))); + customer.setPhone("+3892555554512"); + customer.setState(RandomStringUtils.randomAlphabetic(300)); + doPost("/api/customer", customer).andExpect(statusReason(containsString("length of state must be equal or less than 255"))); + customer.setState("Normal state"); + customer.setZip(RandomStringUtils.randomAlphabetic(300)); + doPost("/api/customer", customer).andExpect(statusReason(containsString("length of zip or postal code must be equal or less than 255"))); + } + @Test public void testUpdateCustomerFromDifferentTenant() throws Exception { Customer customer = new Customer(); diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java index 60b4128878..18fa8bdd5d 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java @@ -93,6 +93,13 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest Assert.assertEquals(foundDashboard.getTitle(), savedDashboard.getTitle()); } + @Test + public void testSaveDashboardInfoWithViolationOfValidation() throws Exception { + Dashboard dashboard = new Dashboard(); + dashboard.setTitle(RandomStringUtils.randomAlphabetic(300)); + doPost("/api/dashboard", dashboard).andExpect(statusReason(containsString("length of title must be equal or less than 255"))); + } + @Test public void testUpdateDashboardFromDifferentTenant() throws Exception { Dashboard dashboard = new Dashboard(); diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java index fa5a6b76eb..c31f5c7daf 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java @@ -114,6 +114,20 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { Assert.assertEquals(foundDevice.getName(), savedDevice.getName()); } + @Test + public void saveDeviceWithViolationOfValidation() throws Exception { + Device device = new Device(); + device.setName(RandomStringUtils.randomAlphabetic(300)); + device.setType("default"); + doPost("/api/device", device).andExpect(statusReason(containsString("length of name must be equal or less than 255"))); + device.setName("Normal Name"); + device.setType(RandomStringUtils.randomAlphabetic(300)); + doPost("/api/device", device).andExpect(statusReason(containsString("length of type must be equal or less than 255"))); + device.setType("Normal type"); + device.setLabel(RandomStringUtils.randomAlphabetic(300)); + doPost("/api/device", device).andExpect(statusReason(containsString("length of label must be equal or less than 255"))); + } + @Test public void testUpdateDeviceFromDifferentTenant() throws Exception { Device device = new Device(); diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java index 3abd9f8e37..d33a93cd64 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java @@ -22,6 +22,7 @@ import com.google.protobuf.DynamicMessage; import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.util.JsonFormat; import com.squareup.wire.schema.internal.parser.ProtoFileElement; +import org.apache.commons.lang3.RandomStringUtils; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -109,6 +110,12 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController Assert.assertEquals(savedDeviceProfile.getName(), foundDeviceProfile.getName()); } + @Test + public void saveDeviceProfileWithViolationOfValidation() throws Exception { + doPost("/api/deviceProfile", this.createDeviceProfile(RandomStringUtils.randomAlphabetic(300), null)) + .andExpect(statusReason(containsString("length of name must be equal or less than 255"))); + } + @Test public void testFindDeviceProfileById() throws Exception { DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", null); diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseEdgeControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseEdgeControllerTest.java index f919804ce9..9259f3253d 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseEdgeControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseEdgeControllerTest.java @@ -48,6 +48,7 @@ import org.thingsboard.server.gen.edge.v1.UserUpdateMsg; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Random; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.nullValue; @@ -112,6 +113,18 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest { Assert.assertEquals(foundEdge.getName(), savedEdge.getName()); } + @Test + public void testSaveEdgeWithViolationOfLengthValidation() throws Exception { + Edge edge = constructEdge(RandomStringUtils.randomAlphabetic(300), "default"); + doPost("/api/edge", edge).andExpect(statusReason(containsString("length of name must be equal or less than 255"))); + edge.setName("normal name"); + edge.setType(RandomStringUtils.randomAlphabetic(300)); + doPost("/api/edge", edge).andExpect(statusReason(containsString("length of type must be equal or less than 255"))); + edge.setType("normal type"); + edge.setLabel(RandomStringUtils.randomAlphabetic(300)); + doPost("/api/edge", edge).andExpect(statusReason(containsString("length of label must be equal or less than 255"))); + } + @Test public void testFindEdgeById() throws Exception { Edge edge = constructEdge("My edge", "default"); diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java index e651caa2ff..304964dfa8 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java @@ -133,6 +133,14 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes assertEquals(foundEntityView.getKeys(), telemetry); } + @Test + public void testSaveEntityViewWithViolationOfValidation() throws Exception { + EntityView entityView = createEntityView(RandomStringUtils.randomAlphabetic(300), 0, 0); + doPost("/api/entityView", entityView).andExpect(statusReason(containsString("length of name must be equal or less than 255"))); + entityView.setName("Normal name"); + entityView.setType(RandomStringUtils.randomAlphabetic(300)); + doPost("/api/entityView", entityView).andExpect(statusReason(containsString("length of type must be equal or less than 255"))); + } @Test public void testUpdateEntityViewFromDifferentTenant() throws Exception { diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseOtaPackageControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseOtaPackageControllerTest.java index 7026548b25..065fc4bab3 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseOtaPackageControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseOtaPackageControllerTest.java @@ -16,6 +16,7 @@ package org.thingsboard.server.controller; import com.fasterxml.jackson.core.type.TypeReference; +import org.apache.commons.lang3.RandomStringUtils; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -40,6 +41,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import static org.hamcrest.Matchers.containsString; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; @@ -117,6 +119,24 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes Assert.assertEquals(foundFirmwareInfo.getTitle(), savedFirmwareInfo.getTitle()); } + @Test + public void saveOtaPackageInfoWithViolationOfLengthValidation() throws Exception { + SaveOtaPackageInfoRequest firmwareInfo = new SaveOtaPackageInfoRequest(); + firmwareInfo.setDeviceProfileId(deviceProfileId); + firmwareInfo.setType(FIRMWARE); + firmwareInfo.setTitle(RandomStringUtils.randomAlphabetic(300)); + firmwareInfo.setVersion(VERSION); + firmwareInfo.setUsesUrl(false); + doPost("/api/otaPackage", firmwareInfo).andExpect(statusReason(containsString("length of title must be equal or less than 255"))); + firmwareInfo.setTitle(TITLE); + firmwareInfo.setVersion(RandomStringUtils.randomAlphabetic(300)); + doPost("/api/otaPackage", firmwareInfo).andExpect(statusReason(containsString("length of version must be equal or less than 255"))); + firmwareInfo.setVersion(VERSION); + firmwareInfo.setUsesUrl(true); + firmwareInfo.setUrl(RandomStringUtils.randomAlphabetic(300)); + doPost("/api/otaPackage", firmwareInfo).andExpect(statusReason(containsString("length of url must be equal or less than 255"))); + } + @Test public void testSaveFirmwareData() throws Exception { SaveOtaPackageInfoRequest firmwareInfo = new SaveOtaPackageInfoRequest(); diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseRuleChainControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseRuleChainControllerTest.java index 67addcd947..6363ca3a67 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseRuleChainControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseRuleChainControllerTest.java @@ -16,6 +16,7 @@ package org.thingsboard.server.controller; import com.fasterxml.jackson.core.type.TypeReference; +import org.apache.commons.lang3.RandomStringUtils; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -33,6 +34,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import static org.hamcrest.Matchers.containsString; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; public abstract class BaseRuleChainControllerTest extends AbstractControllerTest { @@ -84,6 +86,13 @@ public abstract class BaseRuleChainControllerTest extends AbstractControllerTest Assert.assertEquals(savedRuleChain.getName(), foundRuleChain.getName()); } + @Test + public void testSaveRuleChainWithViolationOfLengthValidation() throws Exception { + RuleChain ruleChain = new RuleChain(); + ruleChain.setName(RandomStringUtils.randomAlphabetic(300)); + doPost("/api/ruleChain", ruleChain).andExpect(statusReason(containsString("length of name must be equal or less than 255"))); + } + @Test public void testFindRuleChainById() throws Exception { RuleChain ruleChain = new RuleChain(); diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseTbResourceControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseTbResourceControllerTest.java index 8850b70f86..11a8ff7718 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseTbResourceControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseTbResourceControllerTest.java @@ -16,6 +16,7 @@ package org.thingsboard.server.controller; import com.fasterxml.jackson.core.type.TypeReference; +import org.apache.commons.lang3.RandomStringUtils; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -33,6 +34,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import static org.hamcrest.Matchers.containsString; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; public abstract class BaseTbResourceControllerTest extends AbstractControllerTest { @@ -98,6 +100,16 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes Assert.assertEquals(foundResource.getTitle(), savedResource.getTitle()); } + @Test + public void saveResourceInfoWithViolationOfLengthValidation() throws Exception { + TbResource resource = new TbResource(); + resource.setResourceType(ResourceType.JKS); + resource.setTitle(RandomStringUtils.randomAlphabetic(300)); + resource.setFileName(DEFAULT_FILE_NAME); + resource.setData("Test Data"); + doPost("/api/resource", resource).andExpect(statusReason(containsString("length of title must be equal or less than 255"))); + } + @Test public void testUpdateTbResourceFromDifferentTenant() throws Exception { TbResource resource = new TbResource(); diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseTenantControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseTenantControllerTest.java index b05dd52bb5..51a7d81df8 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseTenantControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseTenantControllerTest.java @@ -52,6 +52,14 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest { doDelete("/api/tenant/"+savedTenant.getId().getId().toString()) .andExpect(status().isOk()); } + + @Test + public void testSaveTenantWithViolationOfValidation() throws Exception { + loginSysAdmin(); + Tenant tenant = new Tenant(); + tenant.setTitle(RandomStringUtils.randomAlphanumeric(300)); + doPost("/api/tenant", tenant).andExpect(statusReason(containsString("length of title must be equal or less than 255"))); + } @Test public void testFindTenantById() throws Exception { diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseTenantProfileControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseTenantProfileControllerTest.java index c70e7d0178..26a84aa6a8 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseTenantProfileControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseTenantProfileControllerTest.java @@ -16,6 +16,7 @@ package org.thingsboard.server.controller; import com.fasterxml.jackson.core.type.TypeReference; +import org.apache.commons.lang3.RandomStringUtils; import org.junit.After; import org.junit.Assert; import org.junit.Test; @@ -74,6 +75,13 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController Assert.assertEquals(foundTenantProfile.getName(), savedTenantProfile.getName()); } + @Test + public void testSaveTenantProfileWithViolationOfLengthValidation() throws Exception { + loginSysAdmin(); + TenantProfile tenantProfile = this.createTenantProfile(RandomStringUtils.randomAlphabetic(300)); + doPost("/api/tenantProfile", tenantProfile).andExpect(statusReason(containsString("length of name must be equal or less than 255"))); + } + @Test public void testFindTenantProfileById() throws Exception { loginSysAdmin(); diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseUserControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseUserControllerTest.java index 600b9656f2..e25b5e35ad 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseUserControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseUserControllerTest.java @@ -105,6 +105,28 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest { .andExpect(status().isOk()); } + @Test + public void testSaveUserWithViolationOfFiledValidation() throws Exception { + loginSysAdmin(); + + Tenant tenant = new Tenant(); + tenant.setTitle("My tenant"); + Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class); + Assert.assertNotNull(savedTenant); + + String email = "tenant2@thingsboard.org"; + User user = new User(); + user.setAuthority(Authority.TENANT_ADMIN); + user.setTenantId(savedTenant.getId()); + user.setEmail(email); + user.setFirstName(RandomStringUtils.randomAlphabetic(300)); + user.setLastName("Downs"); + doPost("/api/user", user).andExpect(statusReason(containsString("Validation error: length of first name must be equal or less than 255"))); + user.setFirstName("Normal name"); + user.setLastName(RandomStringUtils.randomAlphabetic(300)); + doPost("/api/user", user).andExpect(statusReason(containsString("length of last name must be equal or less than 255"))); + } + @Test public void testUpdateUserFromDifferentTenant() throws Exception { loginSysAdmin(); diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseWidgetsBundleControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseWidgetsBundleControllerTest.java index 4b16cee03d..3167466d93 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseWidgetsBundleControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseWidgetsBundleControllerTest.java @@ -16,6 +16,7 @@ package org.thingsboard.server.controller; import com.fasterxml.jackson.core.type.TypeReference; +import org.apache.commons.lang3.RandomStringUtils; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -88,6 +89,13 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController Assert.assertEquals(foundWidgetsBundle.getTitle(), savedWidgetsBundle.getTitle()); } + @Test + public void testSaveWidgetBundleWithViolationOfLengthValidation() throws Exception { + WidgetsBundle widgetsBundle = new WidgetsBundle(); + widgetsBundle.setTitle(RandomStringUtils.randomAlphabetic(300)); + doPost("/api/widgetsBundle", widgetsBundle).andExpect(statusReason(containsString("length of title must be equal or less than 255"))); + } + @Test public void testUpdateWidgetsBundleFromDifferentTenant() throws Exception { WidgetsBundle widgetsBundle = new WidgetsBundle(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/ContactBased.java b/common/data/src/main/java/org/thingsboard/server/common/data/ContactBased.java index a333591e53..4a57a84b1c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/ContactBased.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ContactBased.java @@ -17,30 +17,36 @@ package org.thingsboard.server.common.data; import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.id.UUIDBased; +import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; @EqualsAndHashCode(callSuper = true) public abstract class ContactBased extends SearchTextBasedWithAdditionalInfo implements HasName { - + private static final long serialVersionUID = 5047448057830660988L; + @Length(fieldName = "country") @NoXss protected String country; + @Length(fieldName = "state") @NoXss protected String state; + @Length(fieldName = "city") @NoXss protected String city; @NoXss protected String address; @NoXss protected String address2; + @Length(fieldName = "zip or postal code") @NoXss protected String zip; + @Length(fieldName = "phone") @NoXss protected String phone; @NoXss protected String email; - + public ContactBased() { super(); } @@ -48,7 +54,7 @@ public abstract class ContactBased extends SearchTextBasedW public ContactBased(I id) { super(id); } - + public ContactBased(ContactBased contact) { super(contact); this.country = contact.getCountry(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java b/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java index 14e2ea73c1..170731ca42 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java @@ -22,13 +22,15 @@ import com.fasterxml.jackson.databind.JsonNode; import io.swagger.annotations.ApiModelProperty; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; public class Customer extends ContactBased implements HasTenantId { - + private static final long serialVersionUID = -1599722990298929275L; @NoXss + @Length(fieldName = "title") @ApiModelProperty(position = 3, value = "Title of the customer", example = "Company A") private String title; @ApiModelProperty(position = 5, required = true, value = "JSON object with Tenant Id") @@ -41,7 +43,7 @@ public class Customer extends ContactBased implements HasTenantId { public Customer(CustomerId id) { super(id); } - + public Customer(Customer customer) { super(customer); this.tenantId = customer.getTenantId(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java index 556f13d18e..4463ef9657 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java @@ -21,6 +21,7 @@ import io.swagger.annotations.ApiModelProperty; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; import javax.validation.Valid; @@ -32,6 +33,7 @@ public class DashboardInfo extends SearchTextBased implements HasNa private TenantId tenantId; @NoXss + @Length(fieldName = "title") private String title; private String image; @Valid diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Device.java b/common/data/src/main/java/org/thingsboard/server/common/data/Device.java index a11c05f2b2..7a1a16bfe0 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Device.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Device.java @@ -28,6 +28,7 @@ import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; import java.io.ByteArrayInputStream; @@ -44,10 +45,13 @@ public class Device extends SearchTextBasedWithAdditionalInfo implemen private TenantId tenantId; private CustomerId customerId; @NoXss + @Length(fieldName = "name") private String name; @NoXss + @Length(fieldName = "type") private String type; @NoXss + @Length(fieldName = "label") private String label; private DeviceProfileId deviceProfileId; private transient DeviceData deviceData; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java index 645e4892fc..4c5a2183cb 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java @@ -28,6 +28,7 @@ import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; import javax.validation.Valid; @@ -47,6 +48,7 @@ public class DeviceProfile extends SearchTextBased implements H @ApiModelProperty(position = 3, value = "JSON object with Tenant Id that owns the profile.", readOnly = true) private TenantId tenantId; @NoXss + @Length(fieldName = "name") @ApiModelProperty(position = 4, value = "Unique Device Profile Name in scope of Tenant.", example = "Moisture Sensor") private String name; @NoXss diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java b/common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java index 7f5de9f945..644633e1e6 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java @@ -25,6 +25,7 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.objects.TelemetryEntityView; +import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; /** @@ -44,9 +45,11 @@ public class EntityView extends SearchTextBasedWithAdditionalInfo private TenantId tenantId; private CustomerId customerId; @NoXss + @Length(fieldName = "name") @ApiModelProperty(position = 5, required = true, value = "Entity View name", example = "A4B72CCDFF33") private String name; @NoXss + @Length(fieldName = "type") @ApiModelProperty(position = 6, required = true, value = "Device Profile Name", example = "Temperature Sensor") private String type; @ApiModelProperty(position = 8, required = true, value = "Set of telemetry and attribute keys to expose via Entity View.") diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/OtaPackageInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/OtaPackageInfo.java index 25feeef682..429c78e560 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/OtaPackageInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/OtaPackageInfo.java @@ -27,6 +27,10 @@ import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; import org.thingsboard.server.common.data.ota.OtaPackageType; +import org.thingsboard.server.common.data.validation.Length; +import org.thingsboard.server.common.data.validation.NoXss; +import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; +import org.thingsboard.server.common.data.ota.OtaPackageType; @ApiModel @Slf4j @@ -42,16 +46,26 @@ public class OtaPackageInfo extends SearchTextBasedWithAdditionalInfo implements Has @ApiModelProperty(position = 3, value = "JSON object with Tenant Id. Tenant Id of the resource can't be changed.", readOnly = true) private TenantId tenantId; @NoXss + @Length(fieldName = "title") @ApiModelProperty(position = 4, value = "Resource title.", example = "BinaryAppDataContainer id=19 v1.0") private String title; @ApiModelProperty(position = 5, value = "Resource type.", example = "LWM2M_MODEL", readOnly = true) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Tenant.java b/common/data/src/main/java/org/thingsboard/server/common/data/Tenant.java index f797591ccf..982f18e05c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Tenant.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Tenant.java @@ -23,6 +23,7 @@ import io.swagger.annotations.ApiModelProperty; import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantProfileId; +import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; @ApiModel @@ -31,6 +32,7 @@ public class Tenant extends ContactBased implements HasTenantId { private static final long serialVersionUID = 8057243243859922101L; + @Length(fieldName = "title") @NoXss @ApiModelProperty(position = 3, value = "Title of the tenant", example = "Company A") private String title; @@ -48,7 +50,7 @@ public class Tenant extends ContactBased implements HasTenantId { public Tenant(TenantId id) { super(id); } - + public Tenant(Tenant tenant) { super(tenant); this.title = tenant.getTitle(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java b/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java index 7ca85eecc2..e38555f964 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java @@ -25,6 +25,7 @@ import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.id.TenantProfileId; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; +import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; import java.io.ByteArrayInputStream; @@ -42,6 +43,7 @@ public class TenantProfile extends SearchTextBased implements H private static final long serialVersionUID = 3021989561267192281L; @NoXss + @Length(fieldName = "name") @ApiModelProperty(position = 3, value = "Name of the tenant profile", example = "High Priority Tenants") private String name; @NoXss diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/User.java b/common/data/src/main/java/org/thingsboard/server/common/data/User.java index f6c4c88eec..982e500e0e 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/User.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/User.java @@ -26,7 +26,7 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.security.Authority; - +import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; @ApiModel @@ -40,8 +40,10 @@ public class User extends SearchTextBasedWithAdditionalInfo implements H private String email; private Authority authority; @NoXss + @Length(fieldName = "first name") private String firstName; @NoXss + @Length(fieldName = "last name") private String lastName; public User() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java index b59d62bda6..827da0cb50 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java @@ -30,6 +30,7 @@ import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.validation.Length; import java.util.List; @@ -49,6 +50,7 @@ public class Alarm extends BaseData implements HasName, HasTenantId, Ha private CustomerId customerId; @ApiModelProperty(position = 6, required = true, value = "representing type of the Alarm", example = "High Temperature Alarm") + @Length(fieldName = "type") private String type; @ApiModelProperty(position = 7, required = true, value = "JSON object with alarm originator id") private EntityId originator; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java b/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java index 6caee2f111..2cc2eccd9d 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java @@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; import java.util.Optional; @@ -39,10 +40,13 @@ public class Asset extends SearchTextBasedWithAdditionalInfo implements private TenantId tenantId; private CustomerId customerId; @NoXss + @Length(fieldName = "name") private String name; @NoXss + @Length(fieldName = "type") private String type; @NoXss + @Length(fieldName = "label") private String label; public Asset() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/edge/Edge.java b/common/data/src/main/java/org/thingsboard/server/common/data/edge/Edge.java index 785e5e9bb0..ddb30ffe64 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/edge/Edge.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/edge/Edge.java @@ -28,6 +28,7 @@ import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.validation.Length; @ApiModel @EqualsAndHashCode(callSuper = true) @@ -40,8 +41,11 @@ public class Edge extends SearchTextBasedWithAdditionalInfo implements H private TenantId tenantId; private CustomerId customerId; private RuleChainId rootRuleChainId; + @Length(fieldName = "name") private String name; + @Length(fieldName = "type") private String type; + @Length(fieldName = "label") private String label; private String routingKey; private String secret; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseAttributeKvEntry.java b/common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseAttributeKvEntry.java index d54ef35966..eaca069e95 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseAttributeKvEntry.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseAttributeKvEntry.java @@ -15,8 +15,7 @@ */ package org.thingsboard.server.common.data.kv; -import com.fasterxml.jackson.databind.JsonNode; - +import javax.validation.Valid; import java.util.Optional; /** @@ -27,6 +26,7 @@ public class BaseAttributeKvEntry implements AttributeKvEntry { private static final long serialVersionUID = -6460767583563159407L; private final long lastUpdateTs; + @Valid private final KvEntry kv; public BaseAttributeKvEntry(KvEntry kv, long lastUpdateTs) { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/kv/BasicKvEntry.java b/common/data/src/main/java/org/thingsboard/server/common/data/kv/BasicKvEntry.java index a04c29f82b..9c2edb6250 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/kv/BasicKvEntry.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/kv/BasicKvEntry.java @@ -15,11 +15,14 @@ */ package org.thingsboard.server.common.data.kv; +import org.thingsboard.server.common.data.validation.Length; + import java.util.Objects; import java.util.Optional; public abstract class BasicKvEntry implements KvEntry { + @Length(fieldName = "attribute key") private final String key; protected BasicKvEntry(String key) { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/kv/StringDataEntry.java b/common/data/src/main/java/org/thingsboard/server/common/data/kv/StringDataEntry.java index 407be7ccfa..d7e27e83d1 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/kv/StringDataEntry.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/kv/StringDataEntry.java @@ -21,6 +21,7 @@ import java.util.Optional; public class StringDataEntry extends BasicKvEntry { private static final long serialVersionUID = 1L; + private final String value; public StringDataEntry(String key, String value) { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelation.java b/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelation.java index d3ff9ff5ca..040f99f1f1 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelation.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelation.java @@ -22,6 +22,7 @@ import io.swagger.annotations.ApiModelProperty; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.validation.Length; import java.io.Serializable; @@ -37,6 +38,7 @@ public class EntityRelation implements Serializable { private EntityId from; private EntityId to; + @Length(fieldName = "type") private String type; private RelationTypeGroup typeGroup; private transient JsonNode additionalInfo; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java index 6cfa13a867..bd387db7ee 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java @@ -28,6 +28,7 @@ import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; @ApiModel @@ -41,6 +42,7 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo im @ApiModelProperty(position = 3, required = true, value = "JSON object with Tenant Id.", readOnly = true) private TenantId tenantId; @NoXss + @Length(fieldName = "name") @ApiModelProperty(position = 4, required = true, value = "Rule Chain name", example = "Humidity data processing") private String name; @ApiModelProperty(position = 5, value = "Rule Chain type. 'EDGE' rule chains are processing messages on the edge devices only.", example = "A4B72CCDFF33") diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNode.java b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNode.java index a44595dcfa..8201807808 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNode.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNode.java @@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; +import org.thingsboard.server.common.data.validation.Length; @ApiModel @Data @@ -37,8 +38,10 @@ public class RuleNode extends SearchTextBasedWithAdditionalInfo impl @ApiModelProperty(position = 3, value = "JSON object with the Rule Chain Id. ", readOnly = true) private RuleChainId ruleChainId; + @Length(fieldName = "type") @ApiModelProperty(position = 4, value = "Full Java Class Name of the rule node implementation. ", example = "com.mycompany.iot.rule.engine.ProcessingNode") private String type; + @Length(fieldName = "name") @ApiModelProperty(position = 5, value = "User defined name of the rule node. Used on UI and for logging. ", example = "Process sensor reading") private String name; @ApiModelProperty(position = 6, value = "Enable/disable debug. ", example = "false") diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/validation/Length.java b/common/data/src/main/java/org/thingsboard/server/common/data/validation/Length.java new file mode 100644 index 0000000000..58121de31d --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/validation/Length.java @@ -0,0 +1,38 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * 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 + * + * 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. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.validation; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +@Constraint(validatedBy = {}) +public @interface Length { + String message() default "length of {fieldName} must be equal or less than {max}"; + + String fieldName(); + + int max() default 255; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetsBundle.java b/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetsBundle.java index 17455d1e38..2d04f3d4ef 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetsBundle.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetsBundle.java @@ -23,10 +23,9 @@ import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.SearchTextBased; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.WidgetsBundleId; +import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; -import java.util.Arrays; - @ApiModel public class WidgetsBundle extends SearchTextBased implements HasTenantId { @@ -38,12 +37,14 @@ public class WidgetsBundle extends SearchTextBased implements H private TenantId tenantId; @NoXss + @Length(fieldName = "alias") @Getter @Setter @ApiModelProperty(position = 4, value = "Unique alias that is used in widget types as a reference widget bundle", readOnly = true) private String alias; @NoXss + @Length(fieldName = "title") @Getter @Setter @ApiModelProperty(position = 5, value = "Title used in search and UI", readOnly = true) @@ -55,6 +56,7 @@ public class WidgetsBundle extends SearchTextBased implements H private String image; @NoXss + @Length(fieldName = "description") @Getter @Setter @ApiModelProperty(position = 7, value = "Description", readOnly = true) diff --git a/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java b/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java index b577022d57..2be2331a99 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java @@ -41,6 +41,7 @@ import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.relation.RelationsSearchParameters; import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.service.ConstraintValidator; import javax.annotation.Nullable; import java.util.ArrayList; @@ -559,6 +560,7 @@ public class BaseRelationService implements RelationService { if (relation == null) { throw new DataValidationException("Relation type should be specified!"); } + ConstraintValidator.validateFields(relation); validate(relation.getFrom(), relation.getTo(), relation.getType(), relation.getTypeGroup()); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/ConstraintValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/ConstraintValidator.java new file mode 100644 index 0000000000..30ea042b0a --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/ConstraintValidator.java @@ -0,0 +1,63 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * 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 + * + * 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. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service; + +import lombok.extern.slf4j.Slf4j; +import org.hibernate.validator.HibernateValidator; +import org.hibernate.validator.HibernateValidatorConfiguration; +import org.hibernate.validator.cfg.ConstraintMapping; +import org.thingsboard.server.common.data.validation.Length; +import org.thingsboard.server.common.data.validation.NoXss; + +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.ValidationException; +import javax.validation.Validator; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +@Slf4j +public class ConstraintValidator { + + private static Validator fieldsValidator; + + static { + initializeValidators(); + } + + public static void validateFields(Object data) { + Set> constraintsViolations = fieldsValidator.validate(data); + List validationErrors = constraintsViolations.stream() + .map(ConstraintViolation::getMessage) + .distinct() + .collect(Collectors.toList()); + if (!validationErrors.isEmpty()) { + throw new ValidationException("Validation error: " + String.join(", ", validationErrors)); + } + } + + private static void initializeValidators() { + HibernateValidatorConfiguration validatorConfiguration = Validation.byProvider(HibernateValidator.class).configure(); + + ConstraintMapping constraintMapping = validatorConfiguration.createConstraintMapping(); + constraintMapping.constraintDefinition(NoXss.class).validatedBy(NoXssValidator.class); + constraintMapping.constraintDefinition(Length.class).validatedBy(StringLengthValidator.class); + validatorConfiguration.addMapping(constraintMapping); + + fieldsValidator = validatorConfiguration.buildValidatorFactory().getValidator(); + } +} 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 e630624ed4..a0a7a3be2f 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,51 +17,32 @@ package org.thingsboard.server.dao.service; import com.fasterxml.jackson.databind.JsonNode; import lombok.extern.slf4j.Slf4j; -import org.hibernate.validator.HibernateValidator; -import org.hibernate.validator.HibernateValidatorConfiguration; -import org.hibernate.validator.cfg.ConstraintMapping; import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; -import org.thingsboard.server.common.data.validation.NoXss; import org.thingsboard.server.dao.TenantEntityDao; import org.thingsboard.server.dao.TenantEntityWithDataDao; import org.thingsboard.server.dao.exception.DataValidationException; -import javax.validation.ConstraintViolation; -import javax.validation.Validation; -import javax.validation.Validator; import java.util.HashSet; import java.util.Iterator; -import java.util.List; import java.util.Set; import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; @Slf4j 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 Validator fieldsValidator; - - static { - initializeFieldsValidator(); - } - public void validate(D data, Function tenantIdFunction) { try { if (data == null) { throw new DataValidationException("Data object can't be null!"); } - List validationErrors = validateFields(data); - if (!validationErrors.isEmpty()) { - throw new IllegalArgumentException("Validation error: " + String.join(", ", validationErrors)); - } + ConstraintValidator.validateFields(data); TenantId tenantId = tenantIdFunction.apply(data); validateDataImpl(tenantId, data); @@ -104,14 +85,6 @@ public abstract class DataValidator> { return emailMatcher.matches(); } - private List validateFields(D data) { - Set> constraintsViolations = fieldsValidator.validate(data); - return constraintsViolations.stream() - .map(ConstraintViolation::getMessage) - .distinct() - .collect(Collectors.toList()); - } - protected void validateNumberOfEntitiesPerTenant(TenantId tenantId, TenantEntityDao tenantEntityDao, long maxEntities, @@ -126,10 +99,10 @@ public abstract class DataValidator> { } protected void validateMaxSumDataSizePerTenant(TenantId tenantId, - TenantEntityWithDataDao dataDao, - long maxSumDataSize, - long currentDataSize, - EntityType entityType) { + TenantEntityWithDataDao dataDao, + long maxSumDataSize, + long currentDataSize, + EntityType entityType) { if (maxSumDataSize > 0) { if (dataDao.sumDataSizeByTenantId(tenantId) + currentDataSize > maxSumDataSize) { throw new DataValidationException(String.format("Failed to create the %s, files size limit is exhausted %d bytes!", @@ -156,12 +129,4 @@ public abstract class DataValidator> { } } - private static void initializeFieldsValidator() { - HibernateValidatorConfiguration validatorConfiguration = Validation.byProvider(HibernateValidator.class).configure(); - ConstraintMapping constraintMapping = validatorConfiguration.createConstraintMapping(); - constraintMapping.constraintDefinition(NoXss.class).validatedBy(NoXssValidator.class); - validatorConfiguration.addMapping(constraintMapping); - - fieldsValidator = validatorConfiguration.buildValidatorFactory().getValidator(); - } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/StringLengthValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/StringLengthValidator.java new file mode 100644 index 0000000000..b7d9d3991e --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/StringLengthValidator.java @@ -0,0 +1,41 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * 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 + * + * 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. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service; + +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.validation.Length; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +@Slf4j +public class StringLengthValidator implements ConstraintValidator { + private int max; + + @Override + public boolean isValid(String value, ConstraintValidatorContext context) { + if (StringUtils.isEmpty(value)) { + return true; + } + return value.length() <= max; + } + + @Override + public void initialize(Length constraintAnnotation) { + this.max = constraintAnnotation.max(); + } +} diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOtaPackageServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOtaPackageServiceTest.java index 4f110c73e6..ed6b14dd10 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOtaPackageServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOtaPackageServiceTest.java @@ -38,6 +38,7 @@ import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.dao.exception.DataValidationException; +import javax.validation.ValidationException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collections; @@ -673,8 +674,8 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { firmwareInfo.setUrl(URL); firmwareInfo.setTenantId(tenantId); - thrown.expect(DataValidationException.class); - thrown.expectMessage("The length of title should be equal or shorter than 255"); + thrown.expect(ValidationException.class); + thrown.expectMessage("length of title must be equal or less than 255"); otaPackageService.saveOtaPackageInfo(firmwareInfo, true); } @@ -689,7 +690,7 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { firmwareInfo.setTitle(TITLE); firmwareInfo.setVersion(RandomStringUtils.random(257)); - thrown.expectMessage("The length of version should be equal or shorter than 255"); + thrown.expectMessage("length of version must be equal or less than 255"); otaPackageService.saveOtaPackageInfo(firmwareInfo, true); } diff --git a/ui-ngx/src/app/modules/home/components/attribute/add-attribute-dialog.component.html b/ui-ngx/src/app/modules/home/components/attribute/add-attribute-dialog.component.html index 923589ed39..f64b63fe9f 100644 --- a/ui-ngx/src/app/modules/home/components/attribute/add-attribute-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/attribute/add-attribute-dialog.component.html @@ -36,6 +36,9 @@ {{ 'attribute.key-required' | translate }} + + {{ 'attribute.key-max-length' | translate }} + > exten buildForm(entity: T): FormGroup { const entityForm = this.buildEntityForm(entity); - entityForm.addControl('country', this.fb.control(entity ? entity.country : '', [])); - entityForm.addControl('city', this.fb.control(entity ? entity.city : '', [])); - entityForm.addControl('state', this.fb.control(entity ? entity.state : '', [])); + entityForm.addControl('country', this.fb.control(entity ? entity.country : '', [Validators.maxLength(255)])); + entityForm.addControl('city', this.fb.control(entity ? entity.city : '', [Validators.maxLength(255)])); + entityForm.addControl('state', this.fb.control(entity ? entity.state : '', [Validators.maxLength(255)])); entityForm.addControl('zip', this.fb.control(entity ? entity.zip : '', this.zipValidators(entity ? entity.country : '') )); entityForm.addControl('address', this.fb.control(entity ? entity.address : '', [])); entityForm.addControl('address2', this.fb.control(entity ? entity.address2 : '', [])); - entityForm.addControl('phone', this.fb.control(entity ? entity.phone : '', [])); + entityForm.addControl('phone', this.fb.control(entity ? entity.phone : '', [Validators.maxLength(255)])); entityForm.addControl('email', this.fb.control(entity ? entity.email : '', [Validators.email])); return entityForm; } diff --git a/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.html b/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.html index b91613955d..23fdf04cde 100644 --- a/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.html @@ -41,6 +41,9 @@ {{ 'device-profile.name-required' | translate }} + + {{ 'device-profile.name-max-length' | translate }} + {{ 'device-profile.name-required' | translate }} + + {{ 'device-profile.name-max-length' | translate }} + { }; const form = this.fb.group( { - name: [entity ? entity.name : '', [Validators.required]], + name: [entity ? entity.name : '', [Validators.required, Validators.maxLength(255)]], type: [entity ? entity.type : null, [Validators.required]], image: [entity ? entity.image : null], transportType: [entity ? entity.transportType : null, [Validators.required]], diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.html b/ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.html index e1dd51d1cd..e5a3a54293 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.html @@ -48,6 +48,9 @@ {{ 'tenant-profile.name-required' | translate }} + + {{ 'tenant-profile.name-max-length' | translate }} +
diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.ts b/ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.ts index f8685cc193..24636b565c 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.ts @@ -59,7 +59,7 @@ export class TenantProfileComponent extends EntityComponent { buildForm(entity: TenantProfile): FormGroup { return this.fb.group( { - name: [entity ? entity.name : '', [Validators.required]], + name: [entity ? entity.name : '', [Validators.required, Validators.maxLength(255)]], isolatedTbCore: [entity ? entity.isolatedTbCore : false, []], isolatedTbRuleEngine: [entity ? entity.isolatedTbRuleEngine : false, []], profileData: [entity && !this.isAdd ? entity.profileData : { diff --git a/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html b/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html index 46e83a740e..ae0be158d5 100644 --- a/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html @@ -44,10 +44,16 @@ {{ 'device.name-required' | translate }} + + {{ 'device.name-max-length' | translate }} + device.label + + {{ 'device.label-max-length' | translate }} +
diff --git a/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.ts b/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.ts index 09bd38d999..fdbc25107f 100644 --- a/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.ts @@ -105,8 +105,8 @@ export class DeviceWizardDialogComponent extends private fb: FormBuilder) { super(store, router, dialogRef); this.deviceWizardFormGroup = this.fb.group({ - name: ['', Validators.required], - label: [''], + name: ['', [Validators.required, Validators.maxLength(255)]], + label: ['', Validators.maxLength(255)], gateway: [false], overwriteActivityTime: [false], addProfileType: [0], diff --git a/ui-ngx/src/app/modules/home/pages/admin/resource/resources-library.component.html b/ui-ngx/src/app/modules/home/pages/admin/resource/resources-library.component.html index 1f33d63776..ff2f8a1085 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/resource/resources-library.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/resource/resources-library.component.html @@ -56,6 +56,9 @@ {{ 'resource.title-required' | translate }} + + {{ 'resource.title-max-length' | translate }} + impleme super.ngOnInit(); this.entityForm.get('resourceType').valueChanges.pipe( startWith(ResourceType.LWM2M_MODEL), - pairwise(), + filter(() => this.isAdd), takeUntil(this.destroy$) - ).subscribe(([previousType, type]) => { - if (previousType === this.resourceType.LWM2M_MODEL) { - this.entityForm.get('title').setValidators(Validators.required); - this.entityForm.get('title').updateValueAndValidity({emitEvent: false}); - } + ).subscribe((type) => { if (type === this.resourceType.LWM2M_MODEL) { - this.entityForm.get('title').clearValidators(); - this.entityForm.get('title').updateValueAndValidity({emitEvent: false}); + this.entityForm.get('title').disable({emitEvent: false}); + this.entityForm.patchValue({title: ''}, {emitEvent: false}); + } else { + this.entityForm.get('title').enable({emitEvent: false}) } this.entityForm.patchValue({ data: null, @@ -92,11 +90,8 @@ export class ResourcesLibraryComponent extends EntityComponent impleme buildForm(entity: Resource): FormGroup { const form = this.fb.group( { - title: [entity ? entity.title : '', []], - resourceType: [{ - value: entity?.resourceType ? entity.resourceType : ResourceType.LWM2M_MODEL, - disabled: !this.isAdd - }, [Validators.required]], + title: [entity ? entity.title : "", [Validators.required, Validators.maxLength(255)]], + resourceType: [entity?.resourceType ? entity.resourceType : ResourceType.LWM2M_MODEL, [Validators.required]], fileName: [entity ? entity.fileName : null, [Validators.required]], } ); diff --git a/ui-ngx/src/app/modules/home/pages/asset/asset.component.html b/ui-ngx/src/app/modules/home/pages/asset/asset.component.html index 66b9a152c7..8d786051a7 100644 --- a/ui-ngx/src/app/modules/home/pages/asset/asset.component.html +++ b/ui-ngx/src/app/modules/home/pages/asset/asset.component.html @@ -76,6 +76,9 @@ {{ 'asset.name-required' | translate }} + + {{ 'asset.name-max-length' | translate }} + asset.label + + {{ 'asset.label-max-length' | translate }} +
diff --git a/ui-ngx/src/app/modules/home/pages/asset/asset.component.ts b/ui-ngx/src/app/modules/home/pages/asset/asset.component.ts index 0ce8f8e6e7..af4eae27de 100644 --- a/ui-ngx/src/app/modules/home/pages/asset/asset.component.ts +++ b/ui-ngx/src/app/modules/home/pages/asset/asset.component.ts @@ -66,9 +66,9 @@ export class AssetComponent extends EntityComponent { buildForm(entity: AssetInfo): FormGroup { return this.fb.group( { - name: [entity ? entity.name : '', [Validators.required]], - type: [entity ? entity.type : null, [Validators.required]], - label: [entity ? entity.label : ''], + name: [entity ? entity.name : '', [Validators.required, Validators.maxLength(255)]], + type: [entity ? entity.type : null, [Validators.required, Validators.maxLength(255)]], + label: [entity ? entity.label : '', Validators.maxLength(255)], additionalInfo: this.fb.group( { description: [entity && entity.additionalInfo ? entity.additionalInfo.description : ''], diff --git a/ui-ngx/src/app/modules/home/pages/customer/customer.component.html b/ui-ngx/src/app/modules/home/pages/customer/customer.component.html index 5077950a59..28fc994477 100644 --- a/ui-ngx/src/app/modules/home/pages/customer/customer.component.html +++ b/ui-ngx/src/app/modules/home/pages/customer/customer.component.html @@ -73,6 +73,9 @@ {{ 'customer.title-required' | translate }} + + {{ 'customer.title-max-length' | translate }} +
diff --git a/ui-ngx/src/app/modules/home/pages/customer/customer.component.ts b/ui-ngx/src/app/modules/home/pages/customer/customer.component.ts index 2c6e403767..8ee70baab0 100644 --- a/ui-ngx/src/app/modules/home/pages/customer/customer.component.ts +++ b/ui-ngx/src/app/modules/home/pages/customer/customer.component.ts @@ -58,7 +58,7 @@ export class CustomerComponent extends ContactBasedComponent { buildEntityForm(entity: Customer): FormGroup { return this.fb.group( { - title: [entity ? entity.title : '', [Validators.required]], + title: [entity ? entity.title : '', [Validators.required, Validators.maxLength(255)]], additionalInfo: this.fb.group( { description: [entity && entity.additionalInfo ? entity.additionalInfo.description : ''], diff --git a/ui-ngx/src/app/modules/home/pages/dashboard/dashboard-form.component.html b/ui-ngx/src/app/modules/home/pages/dashboard/dashboard-form.component.html index f9804e7bf3..0d8971395e 100644 --- a/ui-ngx/src/app/modules/home/pages/dashboard/dashboard-form.component.html +++ b/ui-ngx/src/app/modules/home/pages/dashboard/dashboard-form.component.html @@ -104,6 +104,9 @@ {{ 'dashboard.title-required' | translate }} + + {{ 'dashboard.title-max-length' | translate }} +
diff --git a/ui-ngx/src/app/modules/home/pages/dashboard/dashboard-form.component.ts b/ui-ngx/src/app/modules/home/pages/dashboard/dashboard-form.component.ts index 35e6daec22..f027896ffe 100644 --- a/ui-ngx/src/app/modules/home/pages/dashboard/dashboard-form.component.ts +++ b/ui-ngx/src/app/modules/home/pages/dashboard/dashboard-form.component.ts @@ -80,7 +80,7 @@ export class DashboardFormComponent extends EntityComponent { this.updateFields(entity); return this.fb.group( { - title: [entity ? entity.title : '', [Validators.required]], + title: [entity ? entity.title : '', [Validators.required, Validators.maxLength(255)]], image: [entity ? entity.image : null], mobileHide: [entity ? entity.mobileHide : false], mobileOrder: [entity ? entity.mobileOrder : null, [Validators.pattern(/^-?[0-9]+$/)]], diff --git a/ui-ngx/src/app/modules/home/pages/device/device.component.html b/ui-ngx/src/app/modules/home/pages/device/device.component.html index b95dbb6584..1187a34f5d 100644 --- a/ui-ngx/src/app/modules/home/pages/device/device.component.html +++ b/ui-ngx/src/app/modules/home/pages/device/device.component.html @@ -89,6 +89,9 @@ {{ 'device.name-required' | translate }} + + {{ 'device.name-max-length' | translate }} + device.label + + {{ 'device.label-max-length' | translate }} + { buildForm(entity: DeviceInfo): FormGroup { const form = this.fb.group( { - name: [entity ? entity.name : '', [Validators.required]], + name: [entity ? entity.name : '', [Validators.required, Validators.maxLength(255)]], deviceProfileId: [entity ? entity.deviceProfileId : null, [Validators.required]], firmwareId: [entity ? entity.firmwareId : null], softwareId: [entity ? entity.softwareId : null], - label: [entity ? entity.label : ''], + label: [entity ? entity.label : '', [Validators.maxLength(255)]], deviceData: [entity ? entity.deviceData : null, [Validators.required]], additionalInfo: this.fb.group( { diff --git a/ui-ngx/src/app/modules/home/pages/edge/edge.component.html b/ui-ngx/src/app/modules/home/pages/edge/edge.component.html index 41b5363f0b..6d357ffee8 100644 --- a/ui-ngx/src/app/modules/home/pages/edge/edge.component.html +++ b/ui-ngx/src/app/modules/home/pages/edge/edge.component.html @@ -126,6 +126,9 @@ {{ 'edge.name-required' | translate }} + + {{ 'edge.name-max-length' | translate }} + {{ 'edge.edge-license-key-required' | translate }} + + {{ 'edge.type-max-length' | translate }} +
@@ -179,6 +185,9 @@ edge.label + + {{ 'edge.label-max-length' | translate }} +
diff --git a/ui-ngx/src/app/modules/home/pages/edge/edge.component.ts b/ui-ngx/src/app/modules/home/pages/edge/edge.component.ts index b8539df134..397423b863 100644 --- a/ui-ngx/src/app/modules/home/pages/edge/edge.component.ts +++ b/ui-ngx/src/app/modules/home/pages/edge/edge.component.ts @@ -70,9 +70,9 @@ export class EdgeComponent extends EntityComponent { buildForm(entity: EdgeInfo): FormGroup { const form = this.fb.group( { - name: [entity ? entity.name : '', [Validators.required]], - type: [entity?.type ? entity.type : 'default', [Validators.required]], - label: [entity ? entity.label : ''], + name: [entity ? entity.name : '', [Validators.required, Validators.maxLength(255)]], + type: [entity?.type ? entity.type : 'default', [Validators.required, Validators.maxLength(255)]], + label: [entity ? entity.label : '', Validators.maxLength(255)], cloudEndpoint: [null, [Validators.required]], edgeLicenseKey: ['', [Validators.required]], routingKey: this.fb.control({value: entity ? entity.routingKey : null, disabled: true}), diff --git a/ui-ngx/src/app/modules/home/pages/entity-view/entity-view.component.html b/ui-ngx/src/app/modules/home/pages/entity-view/entity-view.component.html index 5ce3b05569..2b3e823327 100644 --- a/ui-ngx/src/app/modules/home/pages/entity-view/entity-view.component.html +++ b/ui-ngx/src/app/modules/home/pages/entity-view/entity-view.component.html @@ -76,6 +76,9 @@ {{ 'entity-view.name-required' | translate }} + + {{ 'entity-view.name-max-length' | translate }} + { buildForm(entity: EntityViewInfo): FormGroup { return this.fb.group( { - name: [entity ? entity.name : '', [Validators.required]], - type: [entity ? entity.type : null, [Validators.required]], + name: [entity ? entity.name : '', [Validators.required, Validators.maxLength(255)]], + type: [entity ? entity.type : null, Validators.required], entityId: [entity ? entity.entityId : null, [Validators.required]], startTimeMs: [entity ? entity.startTimeMs : null], endTimeMs: [entity ? entity.endTimeMs : null], diff --git a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.html b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.html index fa6e0428eb..a710cbb82d 100644 --- a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.html +++ b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.html @@ -65,6 +65,9 @@ {{ 'ota-update.title-required' | translate }} + + {{ 'ota-update.title-max-length' | translate }} + ota-update.version @@ -72,6 +75,9 @@ {{ 'ota-update.version-required' | translate }} + + {{ 'ota-update.version-max-length' | translate }} +
@@ -116,7 +122,8 @@
+ fxLayout.xs="column" fxLayout.md="column" + *ngIf="!(isAdd && this.entityForm.get('generateChecksum').value)"> ota-update.checksum-algorithm @@ -154,7 +161,8 @@ - + ota-update.direct-url-required diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain.component.html b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain.component.html index 66d5d6d74c..a93fac9beb 100644 --- a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain.component.html +++ b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain.component.html @@ -84,6 +84,9 @@ {{ 'rulechain.name-required' | translate }} + + {{ 'rulechain.name-max-length' | translate }} + {{ 'rulechain.debug-mode' | translate }} diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain.component.ts b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain.component.ts index 9aa518e4c0..d38e500324 100644 --- a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain.component.ts +++ b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain.component.ts @@ -58,7 +58,7 @@ export class RuleChainComponent extends EntityComponent { buildForm(entity: RuleChain): FormGroup { return this.fb.group( { - name: [entity ? entity.name : '', [Validators.required]], + name: [entity ? entity.name : '', [Validators.required, Validators.maxLength(255)]], debugMode: [entity ? entity.debugMode : false], additionalInfo: this.fb.group( { diff --git a/ui-ngx/src/app/modules/home/pages/tenant/tenant.component.html b/ui-ngx/src/app/modules/home/pages/tenant/tenant.component.html index eff1ec7bf3..498e1f5eaf 100644 --- a/ui-ngx/src/app/modules/home/pages/tenant/tenant.component.html +++ b/ui-ngx/src/app/modules/home/pages/tenant/tenant.component.html @@ -48,6 +48,9 @@ {{ 'tenant.title-required' | translate }} + + {{ 'tenant.title-max-length' | translate }} + { buildEntityForm(entity: TenantInfo): FormGroup { return this.fb.group( { - title: [entity ? entity.title : '', [Validators.required]], + title: [entity ? entity.title : '', [Validators.required, Validators.maxLength(255)]], tenantProfileId: [entity ? entity.tenantProfileId : null, [Validators.required]], additionalInfo: this.fb.group( { diff --git a/ui-ngx/src/app/modules/home/pages/widget/widgets-bundle.component.html b/ui-ngx/src/app/modules/home/pages/widget/widgets-bundle.component.html index 88fde0e428..6f553a90dc 100644 --- a/ui-ngx/src/app/modules/home/pages/widget/widgets-bundle.component.html +++ b/ui-ngx/src/app/modules/home/pages/widget/widgets-bundle.component.html @@ -44,6 +44,9 @@ {{ 'widgets-bundle.title-required' | translate }} + + {{ 'widgets-bundle.title-max-length' | translate }} + { buildForm(entity: WidgetsBundle): FormGroup { return this.fb.group( { - title: [entity ? entity.title : '', [Validators.required]], + title: [entity ? entity.title : '', [Validators.required, Validators.maxLength(255)]], image: [entity ? entity.image : ''], description: [entity ? entity.description : '', Validators.maxLength(255)] } diff --git a/ui-ngx/src/app/shared/components/contact.component.html b/ui-ngx/src/app/shared/components/contact.component.html index 678232b1cd..e0c09e8ebe 100644 --- a/ui-ngx/src/app/shared/components/contact.component.html +++ b/ui-ngx/src/app/shared/components/contact.component.html @@ -28,10 +28,16 @@ contact.city + + {{ 'contact.city-max-length' | translate }} + contact.state + + {{ 'contact.state-max-length' | translate }} + contact.postal-code @@ -52,6 +58,9 @@ contact.phone + + {{ 'contact.phone-max-length' | translate }} + contact.email diff --git a/ui-ngx/src/app/shared/components/entity/entity-subtype-autocomplete.component.html b/ui-ngx/src/app/shared/components/entity/entity-subtype-autocomplete.component.html index 245a1f2478..69e2a830c7 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-subtype-autocomplete.component.html +++ b/ui-ngx/src/app/shared/components/entity/entity-subtype-autocomplete.component.html @@ -40,4 +40,7 @@ {{ entitySubtypeRequiredText | translate }} + + {{ entitySubtypeMaxLength | translate }} + diff --git a/ui-ngx/src/app/shared/components/entity/entity-subtype-autocomplete.component.ts b/ui-ngx/src/app/shared/components/entity/entity-subtype-autocomplete.component.ts index fa0bcd3810..8a31ea71c6 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-subtype-autocomplete.component.ts +++ b/ui-ngx/src/app/shared/components/entity/entity-subtype-autocomplete.component.ts @@ -15,7 +15,7 @@ /// import { AfterViewInit, Component, ElementRef, forwardRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; -import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { Observable, of, Subscription, throwError } from 'rxjs'; import { catchError, @@ -58,9 +58,11 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor, entityType: EntityType; private requiredValue: boolean; + get required(): boolean { return this.requiredValue; } + @Input() set required(value: boolean) { this.requiredValue = coerceBooleanProperty(value); @@ -74,6 +76,7 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor, selectEntitySubtypeText: string; entitySubtypeText: string; entitySubtypeRequiredText: string; + entitySubtypeMaxLength: string; filteredSubTypes: Observable>; @@ -96,7 +99,7 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor, private entityViewService: EntityViewService, private fb: FormBuilder) { this.subTypeFormGroup = this.fb.group({ - subType: [null] + subType: [null, Validators.maxLength(255)] }); } @@ -114,6 +117,7 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor, this.selectEntitySubtypeText = 'asset.select-asset-type'; this.entitySubtypeText = 'asset.asset-type'; this.entitySubtypeRequiredText = 'asset.asset-type-required'; + this.entitySubtypeMaxLength = 'asset.asset-type-max-length'; this.broadcastSubscription = this.broadcast.on('assetSaved', () => { this.subTypes = null; }); @@ -122,6 +126,7 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor, this.selectEntitySubtypeText = 'device.select-device-type'; this.entitySubtypeText = 'device.device-type'; this.entitySubtypeRequiredText = 'device.device-type-required'; + this.entitySubtypeMaxLength = 'device.device-type-max-length'; this.broadcastSubscription = this.broadcast.on('deviceSaved', () => { this.subTypes = null; }); @@ -130,6 +135,7 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor, this.selectEntitySubtypeText = 'edge.select-edge-type'; this.entitySubtypeText = 'edge.edge-type'; this.entitySubtypeRequiredText = 'edge.edge-type-required'; + this.entitySubtypeMaxLength = 'edge.type-max-length'; this.broadcastSubscription = this.broadcast.on('edgeSaved', () => { this.subTypes = null; }); @@ -138,6 +144,7 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor, this.selectEntitySubtypeText = 'entity-view.select-entity-view-type'; this.entitySubtypeText = 'entity-view.entity-view-type'; this.entitySubtypeRequiredText = 'entity-view.entity-view-type-required'; + this.entitySubtypeMaxLength = 'entity-view.type-max-length' this.broadcastSubscription = this.broadcast.on('entityViewSaved', () => { this.subTypes = null; }); @@ -149,7 +156,7 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor, debounceTime(150), distinctUntilChanged(), tap(value => { - this.updateView(value); + this.updateView(value); }), // startWith(''), map(value => value ? value : ''), @@ -203,7 +210,7 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor, fetchSubTypes(searchText?: string, strictMatch: boolean = false): Observable> { this.searchText = searchText; return this.getSubTypes().pipe( - map(subTypes => subTypes.filter( subType => { + map(subTypes => subTypes.filter(subType => { if (strictMatch) { return searchText ? subType === searchText : false; } else { diff --git a/ui-ngx/src/app/shared/components/relation/relation-type-autocomplete.component.html b/ui-ngx/src/app/shared/components/relation/relation-type-autocomplete.component.html index cd60473b00..b572f6b9df 100644 --- a/ui-ngx/src/app/shared/components/relation/relation-type-autocomplete.component.html +++ b/ui-ngx/src/app/shared/components/relation/relation-type-autocomplete.component.html @@ -39,4 +39,7 @@ {{ 'relation.relation-type-required' | translate }} + + {{ 'relation.relation-type-max-length' | translate }} + diff --git a/ui-ngx/src/app/shared/components/relation/relation-type-autocomplete.component.ts b/ui-ngx/src/app/shared/components/relation/relation-type-autocomplete.component.ts index d425e90b64..4477b9b2f0 100644 --- a/ui-ngx/src/app/shared/components/relation/relation-type-autocomplete.component.ts +++ b/ui-ngx/src/app/shared/components/relation/relation-type-autocomplete.component.ts @@ -68,7 +68,7 @@ export class RelationTypeAutocompleteComponent implements ControlValueAccessor, public translate: TranslateService, private fb: FormBuilder) { this.relationTypeFormGroup = this.fb.group({ - relationType: [null, this.required ? [Validators.required] : []] + relationType: [null, this.required ? [Validators.required, Validators.maxLength(255)] : [Validators.maxLength(255)]] }); } 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 868bae7bbb..ae80aeb230 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -370,6 +370,7 @@ "management": "Asset management", "view-assets": "View Assets", "add": "Add Asset", + "asset-type-max-length": "Asset type should be less than 256", "assign-to-customer": "Assign to customer", "assign-asset-to-customer": "Assign Asset(s) To Customer", "assign-asset-to-customer-text": "Please select the assets to assign to the customer", @@ -392,6 +393,8 @@ "asset-types": "Asset types", "name": "Name", "name-required": "Name is required.", + "name-max-length": "Name should be less than 256", + "label-max-length": "Label should be less than 256", "description": "Description", "type": "Type", "type-required": "Type is required.", @@ -451,6 +454,7 @@ "scope-shared": "Shared attributes", "add": "Add attribute", "key": "Key", + "key-max-length": "Key should be less than 256", "last-update-time": "Last update time", "key-required": "Attribute key is required.", "value": "Value", @@ -592,7 +596,10 @@ "address2": "Address 2", "phone": "Phone", "email": "Email", - "no-address": "No address" + "no-address": "No address", + "state-max-length": "State length should be less than 256", + "phone-max-length": "Phone number should be less than 256", + "city-max-length": "Specified city should be less than 256" }, "common": { "username": "Username", @@ -648,6 +655,7 @@ "manage-dashboards": "Manage dashboards", "title": "Title", "title-required": "Title is required.", + "title-max-length": "Title should be less than 256", "description": "Description", "details": "Details", "events": "Events", @@ -705,6 +713,7 @@ "select-widget-subtitle": "List of available widget types", "delete": "Delete dashboard", "title-required": "Title is required.", + "title-max-length": "Title should be less than 256", "description": "Description", "details": "Details", "dashboard-details": "Dashboard details", @@ -891,6 +900,7 @@ "management": "Device management", "view-devices": "View Devices", "device-alias": "Device alias", + "device-type-max-length": "Device type should be less than 256", "aliases": "Device aliases", "no-alias-matching": "'{{alias}}' not found.", "no-aliases-found": "No aliases found.", @@ -1003,6 +1013,8 @@ "device-types": "Device types", "name": "Name", "name-required": "Name is required.", + "name-max-length": "Name should be less than 256", + "label-max-length": "Label should be less than 256", "description": "Description", "label": "Label", "events": "Events", @@ -1055,6 +1067,7 @@ "set-default": "Make device profile default", "delete": "Delete device profile", "copyId": "Copy device profile Id", + "name-max-length": "Name should be less than 256", "new-device-profile-name": "Device profile name", "new-device-profile-name-required": "Device profile name is required.", "name": "Name", @@ -1416,6 +1429,9 @@ "edge": "Edge", "edge-instances": "Edge instances", "edge-file": "Edge file", + "name-max-length": "Name should be less than 256", + "label-max-length": "Label should be less than 256", + "type-max-length": "Type should be less than 256", "management": "Edge management", "no-edges-matching": "No edges matching '{{entity}}' were found.", "add": "Add Edge", @@ -1750,6 +1766,8 @@ "created-time": "Created time", "name": "Name", "name-required": "Name is required.", + "name-max-length": "Name should be less than 256", + "type-max-length": "Entity view type should be less than 256", "description": "Description", "events": "Events", "details": "Details", @@ -2374,6 +2392,7 @@ "selected-package": "{ count, plural, 1 {1 package} other {# packages} } selected", "title": "Title", "title-required": "Title is required.", + "title-max-length": "Title should be less than 256", "types": { "firmware": "Firmware", "software": "Software" @@ -2384,6 +2403,7 @@ "version-required": "Version is required.", "version-tag": "Version Tag", "version-tag-hint": "Custom tag should match the package version reported by your device.", + "version-max-length": "Version should be less than 256", "warning-after-save-no-edit": "Once the package is uploaded, you will not be able to modify title, version, device profile and package type." }, "position": { @@ -2426,6 +2446,7 @@ "delete": "Delete relation", "relation-type": "Relation type", "relation-type-required": "Relation type is required.", + "relation-type-max-length": "Relation type should be less than 256", "any-relation-type": "Any type", "add": "Add relation", "edit": "Edit relation", @@ -2470,7 +2491,8 @@ "selected-resources": "{ count, plural, 1 {1 resource} other {# resources} } selected", "system": "System", "title": "Title", - "title-required": "Title is required." + "title-required": "Title is required.", + "title-max-length": "Title should be less than 256" }, "rulechain": { "rulechain": "Rule chain", @@ -2479,6 +2501,7 @@ "delete": "Delete rule chain", "name": "Name", "name-required": "Name is required.", + "name-max-length": "Name should be less than 256", "description": "Description", "add": "Add Rule Chain", "set-root": "Make rule chain root", @@ -2621,6 +2644,7 @@ "add-tenant-text": "Add new tenant", "no-tenants-text": "No tenants found", "tenant-details": "Tenant details", + "title-max-length": "Title should be less than 256", "delete-tenant-title": "Are you sure you want to delete the tenant '{{tenantTitle}}'?", "delete-tenant-text": "Be careful, after the confirmation the tenant and all related data will become unrecoverable.", "delete-tenants-title": "Are you sure you want to delete { count, plural, 1 {1 tenant} other {# tenants} }?", @@ -2650,6 +2674,7 @@ "edit": "Edit tenant profile", "tenant-profile-details": "Tenant profile details", "no-tenant-profiles-text": "No tenant profiles found", + "name-max-length": "Name should be less than 256", "search": "Search tenant profiles", "selected-tenant-profiles": "{ count, plural, 1 {1 tenant profile} other {# tenant profiles} } selected", "no-tenant-profiles-matching": "No tenant profile matching '{{entity}}' were found.", @@ -2997,6 +3022,7 @@ "delete": "Delete widgets bundle", "title": "Title", "title-required": "Title is required.", + "title-max-length": "Title should be less than 256", "description": "Description", "image-preview": "Image preview", "add-widgets-bundle-text": "Add new widgets bundle",