From e3d0da90be7735d8049bca1bf5c4800bb959bd23 Mon Sep 17 00:00:00 2001 From: AndrewVolosytnykhThingsboard Date: Thu, 1 Jul 2021 16:21:44 +0300 Subject: [PATCH 01/11] Implement fields validation --- .../server/controller/AdminController.java | 3 +- .../controller/EntityRelationController.java | 2 + .../controller/TelemetryController.java | 141 +++++++++++++----- .../server/common/data/ContactBased.java | 12 +- .../server/common/data/Customer.java | 8 +- .../server/common/data/DashboardInfo.java | 2 + .../server/common/data/Device.java | 4 + .../server/common/data/DeviceProfile.java | 2 + .../server/common/data/EntityView.java | 3 + .../server/common/data/OtaPackageInfo.java | 9 +- .../server/common/data/TbResource.java | 2 + .../server/common/data/TbResourceInfo.java | 2 + .../server/common/data/Tenant.java | 5 +- .../server/common/data/TenantProfile.java | 2 + .../thingsboard/server/common/data/User.java | 4 +- .../server/common/data/alarm/Alarm.java | 2 + .../server/common/data/edge/Edge.java | 5 +- .../common/data/kv/BaseAttributeKvEntry.java | 4 +- .../server/common/data/kv/BasicKvEntry.java | 3 + .../common/data/kv/StringDataEntry.java | 1 + .../common/data/relation/EntityRelation.java | 6 +- .../server/common/data/rule/RuleChain.java | 2 + .../server/common/data/rule/RuleNode.java | 3 + .../server/common/data/validation/Length.java | 38 +++++ .../common/data/widget/WidgetsBundle.java | 4 +- .../dao/service/ConstraintValidator.java | 63 ++++++++ .../server/dao/service/DataValidator.java | 45 +----- .../dao/service/StringLengthValidator.java | 41 +++++ .../add-attribute-dialog.component.html | 3 + .../add-attribute-dialog.component.ts | 2 +- .../entity/contact-based.component.ts | 8 +- .../add-device-profile-dialog.component.html | 3 + .../add-device-profile-dialog.component.ts | 2 +- .../profile/device-profile.component.html | 3 + .../profile/device-profile.component.ts | 2 +- .../profile/tenant-profile.component.html | 3 + .../profile/tenant-profile.component.ts | 2 +- .../device-wizard-dialog.component.html | 6 + .../wizard/device-wizard-dialog.component.ts | 4 +- .../resource/resources-library.component.html | 3 + .../resource/resources-library.component.ts | 23 ++- .../home/pages/asset/asset.component.html | 6 + .../home/pages/asset/asset.component.ts | 6 +- .../pages/customer/customer.component.html | 3 + .../home/pages/customer/customer.component.ts | 2 +- .../dashboard/dashboard-form.component.html | 3 + .../dashboard/dashboard-form.component.ts | 2 +- .../home/pages/device/device.component.html | 6 + .../home/pages/device/device.component.ts | 4 +- .../home/pages/edge/edge.component.html | 9 ++ .../modules/home/pages/edge/edge.component.ts | 6 +- .../entity-view/entity-view.component.html | 3 + .../entity-view/entity-view.component.ts | 4 +- .../ota-update/ota-update.component.html | 12 +- .../pages/rulechain/rulechain.component.html | 3 + .../pages/rulechain/rulechain.component.ts | 2 +- .../home/pages/tenant/tenant.component.html | 3 + .../home/pages/tenant/tenant.component.ts | 2 +- .../widget/widgets-bundle.component.html | 3 + .../pages/widget/widgets-bundle.component.ts | 2 +- .../shared/components/contact.component.html | 9 ++ ...entity-subtype-autocomplete.component.html | 3 + .../entity-subtype-autocomplete.component.ts | 15 +- .../relation-type-autocomplete.component.html | 3 + .../relation-type-autocomplete.component.ts | 2 +- .../assets/locale/locale.constant-en_US.json | 30 +++- 66 files changed, 477 insertions(+), 148 deletions(-) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/validation/Length.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/service/ConstraintValidator.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/service/StringLengthValidator.java diff --git a/application/src/main/java/org/thingsboard/server/controller/AdminController.java b/application/src/main/java/org/thingsboard/server/controller/AdminController.java index 5bd099d07a..2272cbeab4 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AdminController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AdminController.java @@ -26,12 +26,12 @@ import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.rule.engine.api.SmsService; -import org.thingsboard.server.common.data.sms.config.TestSmsRequest; import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.UpdateMessage; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.security.model.SecuritySettings; +import org.thingsboard.server.common.data.sms.config.TestSmsRequest; import org.thingsboard.server.dao.settings.AdminSettingsService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.permission.Operation; @@ -88,6 +88,7 @@ public class AdminController extends BaseController { } else if (adminSettings.getKey().equals("sms")) { smsService.updateSmsConfiguration(); } + return adminSettings; } catch (Exception e) { throw handleException(e); diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java b/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java index 3f072b77f0..e1f3cdf6ad 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java @@ -34,6 +34,7 @@ import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntityRelationInfo; import org.thingsboard.server.common.data.relation.EntityRelationsQuery; import org.thingsboard.server.common.data.relation.RelationTypeGroup; +import org.thingsboard.server.dao.service.ConstraintValidator; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.permission.Operation; @@ -63,6 +64,7 @@ public class EntityRelationController extends BaseController { if (relation.getTypeGroup() == null) { relation.setTypeGroup(RelationTypeGroup.COMMON); } + ConstraintValidator.validateFields(relation); relationService.saveRelation(getTenantId(), relation); logEntityAction(relation.getFrom(), null, getCurrentUser().getCustomerId(), 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 d311cdbfed..7ec626bcdf 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java @@ -48,7 +48,6 @@ import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.exception.ThingsboardException; -import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; @@ -73,6 +72,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; @@ -138,7 +138,11 @@ public class TelemetryController extends BaseController { @ResponseBody public DeferredResult getAttributeKeys( @PathVariable("entityType") String entityType, @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); + } } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @@ -147,8 +151,12 @@ public class TelemetryController extends BaseController { public DeferredResult getAttributeKeysByScope( @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr , @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); + } } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @@ -157,9 +165,13 @@ public class TelemetryController extends BaseController { public DeferredResult getAttributes( @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, @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); + } } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @@ -169,9 +181,13 @@ public class TelemetryController extends BaseController { @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, @PathVariable("scope") String scope, @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); + } } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @@ -179,8 +195,12 @@ public class TelemetryController extends BaseController { @ResponseBody public DeferredResult getTimeseriesKeys( @PathVariable("entityType") String entityType, @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); + } } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @@ -190,10 +210,14 @@ public class TelemetryController extends BaseController { @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, @RequestParam(name = "keys", required = false) String keysStr, @RequestParam(name = "useStrictDataTypes", required = false, defaultValue = "false") Boolean useStrictDataTypes) throws ThingsboardException { - SecurityUser user = getCurrentUser(); + try { + SecurityUser user = getCurrentUser(); - return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, - (result, tenantId, entityId) -> getLatestTimeseriesValuesCallback(result, user, entityId, keysStr, useStrictDataTypes)); + return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, + (result, tenantId, entityId) -> getLatestTimeseriesValuesCallback(result, user, entityId, keysStr, useStrictDataTypes)); + } catch (Exception e) { + throw handleException(e); + } } @@ -211,15 +235,19 @@ public class TelemetryController extends BaseController { @RequestParam(name = "agg", defaultValue = "NONE") String aggStr, @RequestParam(name = "orderBy", defaultValue = "DESC") String orderBy, @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); + } } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @@ -227,8 +255,12 @@ public class TelemetryController extends BaseController { @ResponseBody public DeferredResult saveDeviceAttributes(@PathVariable("deviceId") String deviceIdStr, @PathVariable("scope") String scope, @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); + } } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @@ -237,8 +269,12 @@ public class TelemetryController extends BaseController { public DeferredResult saveEntityAttributesV1(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, @PathVariable("scope") String scope, @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); + } } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @@ -247,8 +283,12 @@ public class TelemetryController extends BaseController { public DeferredResult saveEntityAttributesV2(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, @PathVariable("scope") String scope, @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); + } } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @@ -257,8 +297,12 @@ public class TelemetryController extends BaseController { public DeferredResult saveEntityTelemetry(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, @PathVariable("scope") String scope, @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); + } } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @@ -267,8 +311,12 @@ public class TelemetryController extends BaseController { public DeferredResult saveEntityTelemetryWithTTL(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, @PathVariable("scope") String scope, @PathVariable("ttl") Long ttl, @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); + } } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @@ -280,8 +328,12 @@ public class TelemetryController extends BaseController { @RequestParam(name = "startTs", required = false) Long startTs, @RequestParam(name = "endTs", required = false) Long endTs, @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, @@ -335,8 +387,12 @@ public class TelemetryController extends BaseController { public DeferredResult deleteEntityAttributes(@PathVariable("deviceId") String deviceIdStr, @PathVariable("scope") String scope, @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); + } } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @@ -345,8 +401,12 @@ public class TelemetryController extends BaseController { public DeferredResult deleteEntityAttributes(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, @PathVariable("scope") String scope, @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 { @@ -392,6 +452,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/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 f6f49bb33b..a3dae9ddc9 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 @@ -20,13 +20,15 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty.Access; 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") private String title; private TenantId tenantId; @@ -37,7 +39,7 @@ public class Customer extends ContactBased implements HasTenantId { public Customer(CustomerId id) { super(id); } - + public Customer(Customer customer) { super(customer); this.tenantId = customer.getTenantId(); @@ -51,7 +53,7 @@ public class Customer extends ContactBased implements HasTenantId { public void setTenantId(TenantId tenantId) { this.tenantId = tenantId; } - + public String getTitle() { return title; } 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 cbcd213377..4671de171c 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 @@ -19,6 +19,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; 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; @@ -29,6 +30,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 9abc619b48..35fa50ea32 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 @@ -25,6 +25,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; @@ -39,10 +40,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 13c4692976..29b0a3f23a 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 @@ -26,6 +26,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; @@ -41,6 +42,7 @@ public class DeviceProfile extends SearchTextBased implements H private TenantId tenantId; @NoXss + @Length(fieldName = "name") private String name; @NoXss private String description; 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 e8b48ee23a..7a96419600 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 @@ -23,6 +23,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; /** @@ -41,8 +42,10 @@ public class EntityView extends SearchTextBasedWithAdditionalInfo private TenantId tenantId; private CustomerId customerId; @NoXss + @Length(fieldName = "name") private String name; @NoXss + @Length(fieldName = "type") private String type; private TelemetryEntityView keys; private long startTimeMs; 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 f27c90c20b..be475946cb 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 @@ -19,11 +19,12 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.extern.slf4j.Slf4j; -import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; -import org.thingsboard.server.common.data.ota.OtaPackageType; 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.ota.ChecksumAlgorithm; +import org.thingsboard.server.common.data.ota.OtaPackageType; +import org.thingsboard.server.common.data.validation.Length; @Slf4j @Data @@ -35,10 +36,14 @@ public class OtaPackageInfo extends SearchTextBasedWithAdditionalInfo implements Has private TenantId tenantId; @NoXss + @Length(fieldName = "title") private String title; private ResourceType resourceType; private String resourceKey; 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 b6adf6cf65..9146212361 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 @@ -20,6 +20,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; 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; @EqualsAndHashCode(callSuper = true) @@ -27,8 +28,10 @@ public class Tenant extends ContactBased implements HasTenantId { private static final long serialVersionUID = 8057243243859922101L; + @Length(fieldName = "title") @NoXss private String title; + @Length(fieldName = "region") @NoXss private String region; private TenantProfileId tenantProfileId; @@ -40,7 +43,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 05d22af09f..06597e093c 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 @@ -23,6 +23,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; @@ -37,6 +38,7 @@ import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalIn public class TenantProfile extends SearchTextBased implements HasName { @NoXss + @Length(fieldName = "name") private String name; @NoXss private String description; 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 420aff71ce..a7135ea56f 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 @@ -23,7 +23,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; @EqualsAndHashCode(callSuper = true) @@ -36,8 +36,10 @@ public class User extends SearchTextBasedWithAdditionalInfo implements H private String email; private Authority authority; @NoXss + @Length(fieldName = "firs 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 54f7ab72f5..19fc96b1b8 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 @@ -28,6 +28,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; @@ -41,6 +42,7 @@ public class Alarm extends BaseData implements HasName, HasTenantId, Ha private TenantId tenantId; private CustomerId customerId; + @Length(fieldName = "type") private String type; private EntityId originator; private AlarmSeverity severity; 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 939bfb3648..d612fe44ea 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 @@ -15,7 +15,6 @@ */ package org.thingsboard.server.common.data.edge; -import com.fasterxml.jackson.databind.JsonNode; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; @@ -28,6 +27,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; @EqualsAndHashCode(callSuper = true) @ToString @@ -40,8 +40,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 d87c260898..794e3a16cc 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; /** @@ -25,6 +24,7 @@ import java.util.Optional; public class BaseAttributeKvEntry implements AttributeKvEntry { 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 7833acc050..f8e5930823 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 @@ -16,15 +16,12 @@ package org.thingsboard.server.common.data.relation; import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; 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.ByteArrayInputStream; -import java.io.IOException; import java.io.Serializable; @Slf4j @@ -38,6 +35,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 da84f15427..0b019a7330 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 @@ -26,6 +26,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; @Data @@ -37,6 +38,7 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo im private TenantId tenantId; @NoXss + @Length(fieldName = "name") private String name; private RuleChainType type; private RuleNodeId firstRuleNodeId; 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 6d88ad6adc..8d37473768 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 @@ -24,6 +24,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; @Data @EqualsAndHashCode(callSuper = true) @@ -33,7 +34,9 @@ public class RuleNode extends SearchTextBasedWithAdditionalInfo impl private static final long serialVersionUID = -5656679015121235465L; private RuleChainId ruleChainId; + @Length(fieldName = "type") private String type; + @Length(fieldName = "name") private String name; private boolean debugMode; private transient JsonNode configuration; 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..b6c7f47f5c --- /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} should be equals 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 df09f75922..7b4b172ace 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 @@ -19,10 +19,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; - public class WidgetsBundle extends SearchTextBased implements HasTenantId { private static final long serialVersionUID = -7627368878362410489L; @@ -31,6 +30,7 @@ public class WidgetsBundle extends SearchTextBased implements H @NoXss private String alias; @NoXss + @Length(fieldName = "title") private String title; private String image; @NoXss 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..1efa0192e0 --- /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.trim().length() <= max; + } + + @Override + public void initialize(Length constraintAnnotation) { + this.max = constraintAnnotation.max(); + } +} 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 afdb3eafc1..b5f5be3c9c 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 @@ -58,7 +58,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 68e82fc7e1..bd6b060aab 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, @@ -91,11 +89,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 2ec441c2d3..54279e309c 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 @@ -65,9 +65,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 20e49d118b..a479f8cd22 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 @@ -57,7 +57,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 ca7512d7ec..a26921851d 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 }} + { 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], configuration: this.fb.group( { 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 6ae548acb2..380ca09e93 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 007176ce24..c1113c57ee 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 @@ -69,9 +69,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 be3a2ead31..78559429ae 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 }} +
+ fxLayout.xs="column" fxLayout.md="column" + *ngIf="!(isAdd && this.entityForm.get('generateChecksum').value)"> ota-update.checksum-algorithm @@ -149,7 +156,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 3d8eae1e7a..e7e7d80df1 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 @@ -57,7 +57,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 c68098b8d0..7b2cc80174 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -369,6 +369,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", @@ -391,6 +392,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.", @@ -449,6 +452,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", @@ -590,7 +594,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", @@ -646,6 +653,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", @@ -700,6 +708,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", @@ -886,6 +895,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.", @@ -998,6 +1008,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", @@ -1050,6 +1062,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", @@ -1401,6 +1414,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.", "rulechain-templates": "Rule chain templates", @@ -1741,6 +1757,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", @@ -2335,12 +2353,14 @@ "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" }, "version": "Version", "version-required": "Version is required.", + "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": { @@ -2379,6 +2399,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", @@ -2423,7 +2444,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", @@ -2432,6 +2454,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", @@ -2573,6 +2596,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} }?", @@ -2602,6 +2626,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.", @@ -2925,6 +2950,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", From 4bd3288f57430a4bf30891590b1d5a8fee7276c7 Mon Sep 17 00:00:00 2001 From: AndrewVolosytnykhThingsboard Date: Mon, 19 Jul 2021 12:42:42 +0300 Subject: [PATCH 02/11] Tests for fields validation --- .../controller/BaseAssetControllerTest.java | 14 ++++++++++++ .../BaseCustomerControllerTest.java | 22 +++++++++++++++++++ .../BaseDashboardControllerTest.java | 7 ++++++ .../controller/BaseDeviceControllerTest.java | 14 ++++++++++++ .../BaseDeviceProfileControllerTest.java | 7 ++++++ .../controller/BaseEdgeControllerTest.java | 13 +++++++++++ .../BaseEntityViewControllerTest.java | 8 +++++++ .../BaseOtaPackageControllerTest.java | 20 +++++++++++++++++ .../BaseRuleChainControllerTest.java | 9 ++++++++ .../BaseTbResourceControllerTest.java | 12 ++++++++++ .../controller/BaseTenantControllerTest.java | 13 +++++++++++ .../BaseTenantProfileControllerTest.java | 8 +++++++ .../controller/BaseUserControllerTest.java | 22 +++++++++++++++++++ .../BaseWidgetsBundleControllerTest.java | 8 +++++++ .../server/common/data/Tenant.java | 1 - .../server/common/data/asset/Asset.java | 4 ++++ 16 files changed, 181 insertions(+), 1 deletion(-) 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..e66f4566e8 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 should be equals or less than 255"))); + asset.setName("Normal name"); + asset.setType(RandomStringUtils.randomAlphabetic(300)); + doPost("/api/asset", asset).andExpect(statusReason(containsString("length of type should be equals or less than 255"))); + asset.setType("default"); + asset.setLabel(RandomStringUtils.randomAlphabetic(300)); + doPost("/api/asset", asset).andExpect(statusReason(containsString("length of label should be equals 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..c904230283 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 should be equals or less than 255"))); + customer.setTitle("Normal title"); + customer.setCity(RandomStringUtils.randomAlphabetic(300)); + doPost("/api/customer", customer).andExpect(statusReason(containsString("length of city should be equals or less than 255"))); + customer.setCity("Normal city"); + customer.setCountry(RandomStringUtils.randomAlphabetic(300)); + doPost("/api/customer", customer).andExpect(statusReason(containsString("length of country should be equals or less than 255"))); + customer.setCountry("Ukraine"); + customer.setPhone(RandomStringUtils.randomAlphabetic(300)); + doPost("/api/customer", customer).andExpect(statusReason(containsString("length of phone should be equals or less than 255"))); + customer.setPhone("+3892555554512"); + customer.setState(RandomStringUtils.randomAlphabetic(300)); + doPost("/api/customer", customer).andExpect(statusReason(containsString("length of state should be equals 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 should be equals 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..b16cdb43ac 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 should be equals 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..866d06a6df 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 should be equals or less than 255"))); + device.setName("Normal Name"); + device.setType(RandomStringUtils.randomAlphabetic(300)); + doPost("/api/device", device).andExpect(statusReason(containsString("length of type should be equals or less than 255"))); + device.setType("Normal type"); + device.setLabel(RandomStringUtils.randomAlphabetic(300)); + doPost("/api/device", device).andExpect(statusReason(containsString("length of label should be equals 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 ea5a6dbf86..df2cfd2684 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; @@ -107,6 +108,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 should be equals 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 0d1f6b590d..8e82023455 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseEdgeControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseEdgeControllerTest.java @@ -47,6 +47,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; @@ -111,6 +112,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 should be equals or less than 255"))); + edge.setName("normal name"); + edge.setType(RandomStringUtils.randomAlphabetic(300)); + doPost("/api/edge", edge).andExpect(statusReason(containsString("length of type should be equals or less than 255"))); + edge.setType("normal type"); + edge.setLabel(RandomStringUtils.randomAlphabetic(300)); + doPost("/api/edge", edge).andExpect(statusReason(containsString("length of label should be equals 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..b1193189aa 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 should be equals or less than 255"))); + entityView.setName("Normal name"); + entityView.setType(RandomStringUtils.randomAlphabetic(300)); + doPost("/api/entityView", entityView).andExpect(statusReason(containsString("length of type should be equals 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..7e26e2dc84 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 should be equals or less than 255"))); + firmwareInfo.setTitle(TITLE); + firmwareInfo.setVersion(RandomStringUtils.randomAlphabetic(300)); + doPost("/api/otaPackage", firmwareInfo).andExpect(statusReason(containsString("length of version should be equals 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 should be equals 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..77abee25b7 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 should be equals 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..505e0dc636 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 should be equals 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..f1ee2899a5 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,19 @@ 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 should be equals or less than 255"))); + } + + public static void main(String[] args) { + String temp = RandomStringUtils.randomAlphanumeric(300); + System.err.println(temp); + } @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..885bdd4bd1 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 should be equals 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..946b4deeae 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 firs name should be equals or less than 255"))); + user.setFirstName("Normal name"); + user.setLastName(RandomStringUtils.randomAlphabetic(300)); + doPost("/api/user", user).andExpect(statusReason(containsString("length of last name should be equals 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..4b7fb80ec3 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 should be equals 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/Tenant.java b/common/data/src/main/java/org/thingsboard/server/common/data/Tenant.java index 9146212361..f6145516fe 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 @@ -31,7 +31,6 @@ public class Tenant extends ContactBased implements HasTenantId { @Length(fieldName = "title") @NoXss private String title; - @Length(fieldName = "region") @NoXss private String region; private TenantProfileId tenantProfileId; 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 f9d64cb712..5b9ed91ae2 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 @@ -23,6 +23,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; @EqualsAndHashCode(callSuper = true) @@ -33,10 +34,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() { From 1e3dfc33b2d7811f0d7044a02aea391b8170613a Mon Sep 17 00:00:00 2001 From: Swoq Date: Mon, 30 Aug 2021 17:38:13 +0300 Subject: [PATCH 03/11] Delegation of exception handling logic to the ThingsboardErrorResponseHandler Any thrown Exception casts to the ThingsBoardException. --- .../server/controller/BaseController.java | 23 +++------------- .../controller/TelemetryController.java | 8 ++---- .../ThingsboardErrorResponseHandler.java | 27 ++++++++++++++++--- 3 files changed, 30 insertions(+), 28 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index c37587f546..d120f1b01a 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -281,6 +281,9 @@ public abstract class BaseController { @Autowired protected RuleEngineEntityActionService ruleEngineEntityActionService; + @Autowired + protected ThingsboardErrorResponseHandler thingsboardErrorResponseHandler; + @Value("${server.log_controller_error_stack_trace}") @Getter private boolean logControllerErrorStackTrace; @@ -299,25 +302,7 @@ public abstract class BaseController { } private ThingsboardException handleException(Exception exception, boolean logException) { - if (logException && logControllerErrorStackTrace) { - log.error("Error [{}]", exception.getMessage(), exception); - } - - String cause = ""; - if (exception.getCause() != null) { - cause = exception.getCause().getClass().getCanonicalName(); - } - - if (exception instanceof ThingsboardException) { - return (ThingsboardException) exception; - } else if (exception instanceof IllegalArgumentException || exception instanceof IncorrectParameterException - || exception instanceof DataValidationException || cause.contains("IncorrectParameterException")) { - return new ThingsboardException(exception.getMessage(), ThingsboardErrorCode.BAD_REQUEST_PARAMS); - } else if (exception instanceof MessagingException) { - return new ThingsboardException("Unable to send mail: " + exception.getMessage(), ThingsboardErrorCode.GENERAL); - } else { - return new ThingsboardException(exception.getMessage(), ThingsboardErrorCode.GENERAL); - } + return thingsboardErrorResponseHandler.castToThingsboardException(exception, logException); } T checkNotNull(T reference) throws ThingsboardException { 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 7ec626bcdf..16e25d5c2d 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java @@ -269,12 +269,8 @@ public class TelemetryController extends BaseController { public DeferredResult saveEntityAttributesV1(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, @PathVariable("scope") String scope, @RequestBody JsonNode request) throws ThingsboardException { - try { - EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); - return saveAttributes(getTenantId(), entityId, scope, request); - } catch (Exception e) { - throw handleException(e); - } + EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); + return saveAttributes(getTenantId(), entityId, scope, request); } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") diff --git a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java index 906e87d608..e5cb553a3d 100644 --- a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java +++ b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java @@ -34,10 +34,13 @@ import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExcep import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.msg.tools.TbRateLimitsException; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.service.security.exception.AuthMethodNotSupportedException; import org.thingsboard.server.service.security.exception.JwtExpiredTokenException; import org.thingsboard.server.service.security.exception.UserPasswordExpiredException; +import javax.mail.MessagingException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -85,9 +88,7 @@ public class ThingsboardErrorResponseHandler extends ResponseEntityExceptionHand } else if (exception instanceof AuthenticationException) { handleAuthenticationException((AuthenticationException) exception, response); } else { - response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); - mapper.writeValue(response.getWriter(), ThingsboardErrorResponse.of(exception.getMessage(), - ThingsboardErrorCode.GENERAL, HttpStatus.INTERNAL_SERVER_ERROR)); + handle(castToThingsboardException(exception, true), response); } } catch (IOException e) { log.error("Can't handle exception", e); @@ -95,6 +96,26 @@ public class ThingsboardErrorResponseHandler extends ResponseEntityExceptionHand } } + public ThingsboardException castToThingsboardException(Exception exception, boolean logException) { + if (logException) { + log.error("Error [{}]", exception.getMessage(), exception); + } + + String cause = ""; + if (exception.getCause() != null) { + cause = exception.getCause().getClass().getCanonicalName(); + } + + if (exception instanceof IllegalArgumentException || exception instanceof IncorrectParameterException + || exception instanceof DataValidationException || cause.contains("IncorrectParameterException")) { + return new ThingsboardException(exception.getMessage(), ThingsboardErrorCode.BAD_REQUEST_PARAMS); + } else if (exception instanceof MessagingException) { + return new ThingsboardException("Unable to send mail: " + exception.getMessage(), ThingsboardErrorCode.GENERAL); + } else { + return new ThingsboardException(exception.getMessage(), ThingsboardErrorCode.GENERAL); + } + } + private void handleThingsboardException(ThingsboardException thingsboardException, HttpServletResponse response) throws IOException { From da5758b798eb891c9f748d32a38f420ec67148bc Mon Sep 17 00:00:00 2001 From: Swoq Date: Mon, 30 Aug 2021 18:05:09 +0300 Subject: [PATCH 04/11] ThingsboardErrorResponseHandler now work with: - IllegalArgumentException, IncorrectParameterException, DataValidationException, MessagingException. They aren't considered to be an internal server error now. --- .../server/controller/BaseController.java | 23 +++- .../controller/TelemetryController.java | 130 +++++------------- .../ThingsboardErrorResponseHandler.java | 36 ++--- .../controller/BaseAssetControllerTest.java | 6 +- .../BaseCustomerControllerTest.java | 12 +- .../BaseDashboardControllerTest.java | 2 +- .../controller/BaseDeviceControllerTest.java | 6 +- .../BaseDeviceProfileControllerTest.java | 2 +- .../controller/BaseEdgeControllerTest.java | 6 +- .../BaseEntityViewControllerTest.java | 4 +- .../BaseOtaPackageControllerTest.java | 6 +- .../BaseRuleChainControllerTest.java | 2 +- .../BaseTbResourceControllerTest.java | 2 +- .../controller/BaseTenantControllerTest.java | 2 +- .../BaseTenantProfileControllerTest.java | 2 +- .../controller/BaseUserControllerTest.java | 4 +- .../BaseWidgetsBundleControllerTest.java | 2 +- .../server/common/data/OtaPackageInfo.java | 7 + .../server/common/data/validation/Length.java | 2 +- 19 files changed, 107 insertions(+), 149 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index d120f1b01a..c37587f546 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -281,9 +281,6 @@ public abstract class BaseController { @Autowired protected RuleEngineEntityActionService ruleEngineEntityActionService; - @Autowired - protected ThingsboardErrorResponseHandler thingsboardErrorResponseHandler; - @Value("${server.log_controller_error_stack_trace}") @Getter private boolean logControllerErrorStackTrace; @@ -302,7 +299,25 @@ public abstract class BaseController { } private ThingsboardException handleException(Exception exception, boolean logException) { - return thingsboardErrorResponseHandler.castToThingsboardException(exception, logException); + if (logException && logControllerErrorStackTrace) { + log.error("Error [{}]", exception.getMessage(), exception); + } + + String cause = ""; + if (exception.getCause() != null) { + cause = exception.getCause().getClass().getCanonicalName(); + } + + if (exception instanceof ThingsboardException) { + return (ThingsboardException) exception; + } else if (exception instanceof IllegalArgumentException || exception instanceof IncorrectParameterException + || exception instanceof DataValidationException || cause.contains("IncorrectParameterException")) { + return new ThingsboardException(exception.getMessage(), ThingsboardErrorCode.BAD_REQUEST_PARAMS); + } else if (exception instanceof MessagingException) { + return new ThingsboardException("Unable to send mail: " + exception.getMessage(), ThingsboardErrorCode.GENERAL); + } else { + return new ThingsboardException(exception.getMessage(), ThingsboardErrorCode.GENERAL); + } } T checkNotNull(T reference) throws ThingsboardException { 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 16e25d5c2d..592a332a00 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java @@ -138,11 +138,7 @@ public class TelemetryController extends BaseController { @ResponseBody public DeferredResult getAttributeKeys( @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr) throws ThingsboardException { - try { - return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, this::getAttributeKeysCallback); - } catch (Exception e) { - throw handleException(e); - } + return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, this::getAttributeKeysCallback); } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @@ -151,12 +147,8 @@ public class TelemetryController extends BaseController { public DeferredResult getAttributeKeysByScope( @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr , @PathVariable("scope") String scope) throws ThingsboardException { - try { - return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, - (result, tenantId, entityId) -> getAttributeKeysCallback(result, tenantId, entityId, scope)); - } catch (Exception e) { - throw handleException(e); - } + return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, + (result, tenantId, entityId) -> getAttributeKeysCallback(result, tenantId, entityId, scope)); } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @@ -165,13 +157,9 @@ public class TelemetryController extends BaseController { public DeferredResult getAttributes( @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException { - 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); - } + SecurityUser user = getCurrentUser(); + return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, + (result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, null, keysStr)); } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @@ -181,13 +169,9 @@ public class TelemetryController extends BaseController { @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, @PathVariable("scope") String scope, @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException { - 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); - } + SecurityUser user = getCurrentUser(); + return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, + (result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, scope, keysStr)); } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @@ -195,12 +179,8 @@ public class TelemetryController extends BaseController { @ResponseBody public DeferredResult getTimeseriesKeys( @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr) throws ThingsboardException { - 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); - } + return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, + (result, tenantId, entityId) -> Futures.addCallback(tsService.findAllLatest(tenantId, entityId), getTsKeysToResponseCallback(result), MoreExecutors.directExecutor())); } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @@ -210,14 +190,10 @@ public class TelemetryController extends BaseController { @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, @RequestParam(name = "keys", required = false) String keysStr, @RequestParam(name = "useStrictDataTypes", required = false, defaultValue = "false") Boolean useStrictDataTypes) throws ThingsboardException { - try { - SecurityUser user = getCurrentUser(); + 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); - } + return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, + (result, tenantId, entityId) -> getLatestTimeseriesValuesCallback(result, user, entityId, keysStr, useStrictDataTypes)); } @@ -235,19 +211,15 @@ public class TelemetryController extends BaseController { @RequestParam(name = "agg", defaultValue = "NONE") String aggStr, @RequestParam(name = "orderBy", defaultValue = "DESC") String orderBy, @RequestParam(name = "useStrictDataTypes", required = false, defaultValue = "false") Boolean useStrictDataTypes) throws ThingsboardException { - 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); - } + 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()); + }); } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @@ -255,12 +227,8 @@ public class TelemetryController extends BaseController { @ResponseBody public DeferredResult saveDeviceAttributes(@PathVariable("deviceId") String deviceIdStr, @PathVariable("scope") String scope, @RequestBody JsonNode request) throws ThingsboardException { - try { - EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr); - return saveAttributes(getTenantId(), entityId, scope, request); - } catch (Exception e) { - throw handleException(e); - } + EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr); + return saveAttributes(getTenantId(), entityId, scope, request); } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @@ -279,12 +247,8 @@ public class TelemetryController extends BaseController { public DeferredResult saveEntityAttributesV2(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, @PathVariable("scope") String scope, @RequestBody JsonNode request) throws ThingsboardException { - try { - EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); - return saveAttributes(getTenantId(), entityId, scope, request); - } catch (Exception e) { - throw handleException(e); - } + EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); + return saveAttributes(getTenantId(), entityId, scope, request); } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @@ -293,12 +257,8 @@ public class TelemetryController extends BaseController { public DeferredResult saveEntityTelemetry(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, @PathVariable("scope") String scope, @RequestBody String requestBody) throws ThingsboardException { - try { - EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); - return saveTelemetry(getTenantId(), entityId, requestBody, 0L); - } catch (Exception e) { - throw handleException(e); - } + EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); + return saveTelemetry(getTenantId(), entityId, requestBody, 0L); } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @@ -307,12 +267,8 @@ public class TelemetryController extends BaseController { public DeferredResult saveEntityTelemetryWithTTL(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, @PathVariable("scope") String scope, @PathVariable("ttl") Long ttl, @RequestBody String requestBody) throws ThingsboardException { - try { - EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); - return saveTelemetry(getTenantId(), entityId, requestBody, ttl); - } catch (Exception e) { - throw handleException(e); - } + EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); + return saveTelemetry(getTenantId(), entityId, requestBody, ttl); } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @@ -324,12 +280,8 @@ public class TelemetryController extends BaseController { @RequestParam(name = "startTs", required = false) Long startTs, @RequestParam(name = "endTs", required = false) Long endTs, @RequestParam(name = "rewriteLatestIfDeleted", defaultValue = "false") boolean rewriteLatestIfDeleted) throws ThingsboardException { - try { - EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); - return deleteTimeseries(entityId, keysStr, deleteAllDataForKeys, startTs, endTs, rewriteLatestIfDeleted); - } catch (Exception e) { - throw handleException(e); - } + EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); + return deleteTimeseries(entityId, keysStr, deleteAllDataForKeys, startTs, endTs, rewriteLatestIfDeleted); } private DeferredResult deleteTimeseries(EntityId entityIdStr, String keysStr, boolean deleteAllDataForKeys, @@ -383,12 +335,8 @@ public class TelemetryController extends BaseController { public DeferredResult deleteEntityAttributes(@PathVariable("deviceId") String deviceIdStr, @PathVariable("scope") String scope, @RequestParam(name = "keys") String keysStr) throws ThingsboardException { - try { - EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr); - return deleteAttributes(entityId, scope, keysStr); - } catch (Exception e) { - throw handleException(e); - } + EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr); + return deleteAttributes(entityId, scope, keysStr); } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @@ -397,12 +345,8 @@ public class TelemetryController extends BaseController { public DeferredResult deleteEntityAttributes(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, @PathVariable("scope") String scope, @RequestParam(name = "keys") String keysStr) throws ThingsboardException { - try { - EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); - return deleteAttributes(entityId, scope, keysStr); - } catch (Exception e) { - throw handleException(e); - } + EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); + return deleteAttributes(entityId, scope, keysStr); } private DeferredResult deleteAttributes(EntityId entityIdSrc, String scope, String keysStr) throws ThingsboardException { diff --git a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java index e5cb553a3d..225cfc9097 100644 --- a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java +++ b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java @@ -74,6 +74,17 @@ public class ThingsboardErrorResponseHandler extends ResponseEntityExceptionHand try { response.setContentType(MediaType.APPLICATION_JSON_VALUE); + String cause = ""; + if (exception.getCause() != null) { + cause = exception.getCause().getClass().getCanonicalName(); + } + if (exception instanceof IllegalArgumentException || exception instanceof IncorrectParameterException + || exception instanceof DataValidationException || cause.contains("IncorrectParameterException")) { + exception = new ThingsboardException(exception.getMessage(), ThingsboardErrorCode.BAD_REQUEST_PARAMS); + } else if (exception instanceof MessagingException) { + exception = new ThingsboardException("Unable to send mail: " + exception.getMessage(), ThingsboardErrorCode.GENERAL); + } + if (exception instanceof ThingsboardException) { ThingsboardException thingsboardException = (ThingsboardException) exception; if (thingsboardException.getErrorCode() == ThingsboardErrorCode.SUBSCRIPTION_VIOLATION) { @@ -88,7 +99,9 @@ public class ThingsboardErrorResponseHandler extends ResponseEntityExceptionHand } else if (exception instanceof AuthenticationException) { handleAuthenticationException((AuthenticationException) exception, response); } else { - handle(castToThingsboardException(exception, true), response); + response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); + mapper.writeValue(response.getWriter(), ThingsboardErrorResponse.of(exception.getMessage(), + ThingsboardErrorCode.GENERAL, HttpStatus.INTERNAL_SERVER_ERROR)); } } catch (IOException e) { log.error("Can't handle exception", e); @@ -96,27 +109,6 @@ public class ThingsboardErrorResponseHandler extends ResponseEntityExceptionHand } } - public ThingsboardException castToThingsboardException(Exception exception, boolean logException) { - if (logException) { - log.error("Error [{}]", exception.getMessage(), exception); - } - - String cause = ""; - if (exception.getCause() != null) { - cause = exception.getCause().getClass().getCanonicalName(); - } - - if (exception instanceof IllegalArgumentException || exception instanceof IncorrectParameterException - || exception instanceof DataValidationException || cause.contains("IncorrectParameterException")) { - return new ThingsboardException(exception.getMessage(), ThingsboardErrorCode.BAD_REQUEST_PARAMS); - } else if (exception instanceof MessagingException) { - return new ThingsboardException("Unable to send mail: " + exception.getMessage(), ThingsboardErrorCode.GENERAL); - } else { - return new ThingsboardException(exception.getMessage(), ThingsboardErrorCode.GENERAL); - } - } - - private void handleThingsboardException(ThingsboardException thingsboardException, HttpServletResponse response) throws IOException { ThingsboardErrorCode errorCode = thingsboardException.getErrorCode(); 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 e66f4566e8..c72b732ff6 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java @@ -104,13 +104,13 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { Asset asset = new Asset(); asset.setName(RandomStringUtils.randomAlphabetic(300)); asset.setType("default"); - doPost("/api/asset", asset).andExpect(statusReason(containsString("length of name should be equals or less than 255"))); + 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 should be equals or less than 255"))); + 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 should be equals or less than 255"))); + doPost("/api/asset", asset).andExpect(statusReason(containsString("length of label must be equal or less than 255"))); } @Test 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 c904230283..318e1413a0 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java @@ -94,22 +94,22 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest public void testSaveCustomerWithViolationOfValidation() throws Exception { Customer customer = new Customer(); customer.setTitle(RandomStringUtils.randomAlphabetic(300)); - doPost("/api/customer", customer).andExpect(statusReason(containsString("length of title should be equals or less than 255"))); + 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 should be equals or less than 255"))); + 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 should be equals or less than 255"))); + 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 should be equals or less than 255"))); + 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 should be equals or less than 255"))); + 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 should be equals or less than 255"))); + doPost("/api/customer", customer).andExpect(statusReason(containsString("length of zip or postal code must be equal or less than 255"))); } @Test 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 b16cdb43ac..18fa8bdd5d 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java @@ -97,7 +97,7 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest public void testSaveDashboardInfoWithViolationOfValidation() throws Exception { Dashboard dashboard = new Dashboard(); dashboard.setTitle(RandomStringUtils.randomAlphabetic(300)); - doPost("/api/dashboard", dashboard).andExpect(statusReason(containsString("length of title should be equals or less than 255"))); + doPost("/api/dashboard", dashboard).andExpect(statusReason(containsString("length of title must be equal or less than 255"))); } @Test 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 866d06a6df..c31f5c7daf 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java @@ -119,13 +119,13 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { Device device = new Device(); device.setName(RandomStringUtils.randomAlphabetic(300)); device.setType("default"); - doPost("/api/device", device).andExpect(statusReason(containsString("length of name should be equals or less than 255"))); + 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 should be equals or less than 255"))); + 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 should be equals or less than 255"))); + doPost("/api/device", device).andExpect(statusReason(containsString("length of label must be equal or less than 255"))); } @Test 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 8696c7e499..d33a93cd64 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java @@ -113,7 +113,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController @Test public void saveDeviceProfileWithViolationOfValidation() throws Exception { doPost("/api/deviceProfile", this.createDeviceProfile(RandomStringUtils.randomAlphabetic(300), null)) - .andExpect(statusReason(containsString("length of name should be equals or less than 255"))); + .andExpect(statusReason(containsString("length of name must be equal or less than 255"))); } @Test 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 40529d40e8..9259f3253d 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseEdgeControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseEdgeControllerTest.java @@ -116,13 +116,13 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest { @Test public void testSaveEdgeWithViolationOfLengthValidation() throws Exception { Edge edge = constructEdge(RandomStringUtils.randomAlphabetic(300), "default"); - doPost("/api/edge", edge).andExpect(statusReason(containsString("length of name should be equals or less than 255"))); + 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 should be equals or less than 255"))); + 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 should be equals or less than 255"))); + doPost("/api/edge", edge).andExpect(statusReason(containsString("length of label must be equal or less than 255"))); } @Test 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 b1193189aa..304964dfa8 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java @@ -136,10 +136,10 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes @Test public void testSaveEntityViewWithViolationOfValidation() throws Exception { EntityView entityView = createEntityView(RandomStringUtils.randomAlphabetic(300), 0, 0); - doPost("/api/entityView", entityView).andExpect(statusReason(containsString("length of name should be equals or less than 255"))); + 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 should be equals or less than 255"))); + doPost("/api/entityView", entityView).andExpect(statusReason(containsString("length of type must be equal or less than 255"))); } @Test 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 7e26e2dc84..065fc4bab3 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseOtaPackageControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseOtaPackageControllerTest.java @@ -127,14 +127,14 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes firmwareInfo.setTitle(RandomStringUtils.randomAlphabetic(300)); firmwareInfo.setVersion(VERSION); firmwareInfo.setUsesUrl(false); - doPost("/api/otaPackage", firmwareInfo).andExpect(statusReason(containsString("length of title should be equals or less than 255"))); + 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 should be equals or less than 255"))); + 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 should be equals or less than 255"))); + doPost("/api/otaPackage", firmwareInfo).andExpect(statusReason(containsString("length of url must be equal or less than 255"))); } @Test 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 77abee25b7..6363ca3a67 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseRuleChainControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseRuleChainControllerTest.java @@ -90,7 +90,7 @@ public abstract class BaseRuleChainControllerTest extends AbstractControllerTest public void testSaveRuleChainWithViolationOfLengthValidation() throws Exception { RuleChain ruleChain = new RuleChain(); ruleChain.setName(RandomStringUtils.randomAlphabetic(300)); - doPost("/api/ruleChain", ruleChain).andExpect(statusReason(containsString("length of name should be equals or less than 255"))); + doPost("/api/ruleChain", ruleChain).andExpect(statusReason(containsString("length of name must be equal or less than 255"))); } @Test 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 505e0dc636..11a8ff7718 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseTbResourceControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseTbResourceControllerTest.java @@ -107,7 +107,7 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes resource.setTitle(RandomStringUtils.randomAlphabetic(300)); resource.setFileName(DEFAULT_FILE_NAME); resource.setData("Test Data"); - doPost("/api/resource", resource).andExpect(statusReason(containsString("length of title should be equals or less than 255"))); + doPost("/api/resource", resource).andExpect(statusReason(containsString("length of title must be equal or less than 255"))); } @Test 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 f1ee2899a5..9db4c1aab0 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseTenantControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseTenantControllerTest.java @@ -58,7 +58,7 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest { loginSysAdmin(); Tenant tenant = new Tenant(); tenant.setTitle(RandomStringUtils.randomAlphanumeric(300)); - doPost("/api/tenant", tenant).andExpect(statusReason(containsString("length of title should be equals or less than 255"))); + doPost("/api/tenant", tenant).andExpect(statusReason(containsString("length of title must be equal or less than 255"))); } public static void main(String[] args) { 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 885bdd4bd1..26a84aa6a8 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseTenantProfileControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseTenantProfileControllerTest.java @@ -79,7 +79,7 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController public void testSaveTenantProfileWithViolationOfLengthValidation() throws Exception { loginSysAdmin(); TenantProfile tenantProfile = this.createTenantProfile(RandomStringUtils.randomAlphabetic(300)); - doPost("/api/tenantProfile", tenantProfile).andExpect(statusReason(containsString("length of name should be equals or less than 255"))); + doPost("/api/tenantProfile", tenantProfile).andExpect(statusReason(containsString("length of name must be equal or less than 255"))); } @Test 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 946b4deeae..7e378ea2dd 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseUserControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseUserControllerTest.java @@ -121,10 +121,10 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest { user.setEmail(email); user.setFirstName(RandomStringUtils.randomAlphabetic(300)); user.setLastName("Downs"); - doPost("/api/user", user).andExpect(statusReason(containsString("Validation error: length of firs name should be equals or less than 255"))); + doPost("/api/user", user).andExpect(statusReason(containsString("Validation error: length of firs 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 should be equals or less than 255"))); + doPost("/api/user", user).andExpect(statusReason(containsString("length of last name must be equal or less than 255"))); } @Test 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 4b7fb80ec3..3167466d93 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseWidgetsBundleControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseWidgetsBundleControllerTest.java @@ -93,7 +93,7 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController public void testSaveWidgetBundleWithViolationOfLengthValidation() throws Exception { WidgetsBundle widgetsBundle = new WidgetsBundle(); widgetsBundle.setTitle(RandomStringUtils.randomAlphabetic(300)); - doPost("/api/widgetsBundle", widgetsBundle).andExpect(statusReason(containsString("length of title should be equals or less than 255"))); + doPost("/api/widgetsBundle", widgetsBundle).andExpect(statusReason(containsString("length of title must be equal or less than 255"))); } @Test 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 a442022e8d..75c8c4b289 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 @@ -25,6 +25,7 @@ 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; @Slf4j @Data @@ -37,14 +38,20 @@ public class OtaPackageInfo extends SearchTextBasedWithAdditionalInfo Date: Wed, 1 Sep 2021 17:52:39 +0300 Subject: [PATCH 05/11] Tests fix --- .../server/controller/BaseTenantControllerTest.java | 5 ----- .../server/dao/service/BaseOtaPackageServiceTest.java | 4 ++-- 2 files changed, 2 insertions(+), 7 deletions(-) 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 9db4c1aab0..51a7d81df8 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseTenantControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseTenantControllerTest.java @@ -60,11 +60,6 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest { tenant.setTitle(RandomStringUtils.randomAlphanumeric(300)); doPost("/api/tenant", tenant).andExpect(statusReason(containsString("length of title must be equal or less than 255"))); } - - public static void main(String[] args) { - String temp = RandomStringUtils.randomAlphanumeric(300); - System.err.println(temp); - } @Test public void testFindTenantById() throws Exception { 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 1bf71370ea..c7c41f69be 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 @@ -673,7 +673,7 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { firmwareInfo.setTenantId(tenantId); thrown.expect(DataValidationException.class); - thrown.expectMessage("The length of title should be equal or shorter than 255"); + thrown.expectMessage("The length of title must be equal or shorter than 255"); otaPackageService.saveOtaPackageInfo(firmwareInfo, true); } @@ -688,7 +688,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("The length of version must be equal or shorter than 255"); otaPackageService.saveOtaPackageInfo(firmwareInfo, true); } From 6f1a5ea54a353905629d2a5375f0308353e0e87c Mon Sep 17 00:00:00 2001 From: Swoq Date: Wed, 1 Sep 2021 18:12:17 +0300 Subject: [PATCH 06/11] Tests fix --- .../server/dao/service/BaseOtaPackageServiceTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 c7c41f69be..10bc49ac28 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 @@ -673,7 +673,7 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { firmwareInfo.setTenantId(tenantId); thrown.expect(DataValidationException.class); - thrown.expectMessage("The length of title must be equal or shorter than 255"); + thrown.expectMessage("length of title must be equal or less than 255"); otaPackageService.saveOtaPackageInfo(firmwareInfo, true); } @@ -688,7 +688,7 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { firmwareInfo.setTitle(TITLE); firmwareInfo.setVersion(RandomStringUtils.random(257)); - thrown.expectMessage("The length of version must be equal or shorter than 255"); + thrown.expectMessage("length of version must be equal or less than 255"); otaPackageService.saveOtaPackageInfo(firmwareInfo, true); } From 57cec85bb1e4fd8a965a545a1dc2de2bc48d5c4c Mon Sep 17 00:00:00 2001 From: Swoq Date: Wed, 1 Sep 2021 18:31:09 +0300 Subject: [PATCH 07/11] Tests fix --- .../server/dao/service/BaseOtaPackageServiceTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 10bc49ac28..af8f182d8b 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; @@ -672,7 +673,7 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { firmwareInfo.setUrl(URL); firmwareInfo.setTenantId(tenantId); - thrown.expect(DataValidationException.class); + thrown.expect(ValidationException.class); thrown.expectMessage("length of title must be equal or less than 255"); otaPackageService.saveOtaPackageInfo(firmwareInfo, true); From 827da31d92bf52a3a25e06ecaf83a2497395b0b6 Mon Sep 17 00:00:00 2001 From: Swoq Date: Thu, 2 Sep 2021 10:23:02 +0300 Subject: [PATCH 08/11] Validation fixes --- .../src/main/java/org/thingsboard/server/common/data/User.java | 2 +- .../thingsboard/server/common/data/widget/WidgetsBundle.java | 1 + .../thingsboard/server/dao/service/StringLengthValidator.java | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) 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 a7135ea56f..ab334dfeab 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 @@ -36,7 +36,7 @@ public class User extends SearchTextBasedWithAdditionalInfo implements H private String email; private Authority authority; @NoXss - @Length(fieldName = "firs name") + @Length(fieldName = "first name") private String firstName; @NoXss @Length(fieldName = "last name") 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 fcf3eb9fac..468b409c3e 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 @@ -28,6 +28,7 @@ public class WidgetsBundle extends SearchTextBased implements H private TenantId tenantId; @NoXss + @Length(fieldName = "alias") private String alias; @NoXss @Length(fieldName = "title") 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 index 1efa0192e0..b7d9d3991e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/StringLengthValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/StringLengthValidator.java @@ -31,7 +31,7 @@ public class StringLengthValidator implements ConstraintValidator Date: Thu, 2 Sep 2021 16:26:58 +0300 Subject: [PATCH 09/11] Fix logging exceptions of controllers --- .../controller/TelemetryController.java | 138 +++++++++++++----- .../ThingsboardErrorResponseHandler.java | 14 -- .../common/data/widget/WidgetsBundle.java | 1 + 3 files changed, 100 insertions(+), 53 deletions(-) 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 592a332a00..7ec626bcdf 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java @@ -138,7 +138,11 @@ public class TelemetryController extends BaseController { @ResponseBody public DeferredResult getAttributeKeys( @PathVariable("entityType") String entityType, @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); + } } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @@ -147,8 +151,12 @@ public class TelemetryController extends BaseController { public DeferredResult getAttributeKeysByScope( @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr , @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); + } } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @@ -157,9 +165,13 @@ public class TelemetryController extends BaseController { public DeferredResult getAttributes( @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, @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); + } } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @@ -169,9 +181,13 @@ public class TelemetryController extends BaseController { @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, @PathVariable("scope") String scope, @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); + } } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @@ -179,8 +195,12 @@ public class TelemetryController extends BaseController { @ResponseBody public DeferredResult getTimeseriesKeys( @PathVariable("entityType") String entityType, @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); + } } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @@ -190,10 +210,14 @@ public class TelemetryController extends BaseController { @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, @RequestParam(name = "keys", required = false) String keysStr, @RequestParam(name = "useStrictDataTypes", required = false, defaultValue = "false") Boolean useStrictDataTypes) throws ThingsboardException { - SecurityUser user = getCurrentUser(); + try { + SecurityUser user = getCurrentUser(); - return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, - (result, tenantId, entityId) -> getLatestTimeseriesValuesCallback(result, user, entityId, keysStr, useStrictDataTypes)); + return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, + (result, tenantId, entityId) -> getLatestTimeseriesValuesCallback(result, user, entityId, keysStr, useStrictDataTypes)); + } catch (Exception e) { + throw handleException(e); + } } @@ -211,15 +235,19 @@ public class TelemetryController extends BaseController { @RequestParam(name = "agg", defaultValue = "NONE") String aggStr, @RequestParam(name = "orderBy", defaultValue = "DESC") String orderBy, @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); + } } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @@ -227,8 +255,12 @@ public class TelemetryController extends BaseController { @ResponseBody public DeferredResult saveDeviceAttributes(@PathVariable("deviceId") String deviceIdStr, @PathVariable("scope") String scope, @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); + } } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @@ -237,8 +269,12 @@ public class TelemetryController extends BaseController { public DeferredResult saveEntityAttributesV1(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, @PathVariable("scope") String scope, @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); + } } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @@ -247,8 +283,12 @@ public class TelemetryController extends BaseController { public DeferredResult saveEntityAttributesV2(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, @PathVariable("scope") String scope, @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); + } } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @@ -257,8 +297,12 @@ public class TelemetryController extends BaseController { public DeferredResult saveEntityTelemetry(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, @PathVariable("scope") String scope, @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); + } } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @@ -267,8 +311,12 @@ public class TelemetryController extends BaseController { public DeferredResult saveEntityTelemetryWithTTL(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, @PathVariable("scope") String scope, @PathVariable("ttl") Long ttl, @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); + } } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @@ -280,8 +328,12 @@ public class TelemetryController extends BaseController { @RequestParam(name = "startTs", required = false) Long startTs, @RequestParam(name = "endTs", required = false) Long endTs, @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, @@ -335,8 +387,12 @@ public class TelemetryController extends BaseController { public DeferredResult deleteEntityAttributes(@PathVariable("deviceId") String deviceIdStr, @PathVariable("scope") String scope, @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); + } } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @@ -345,8 +401,12 @@ public class TelemetryController extends BaseController { public DeferredResult deleteEntityAttributes(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, @PathVariable("scope") String scope, @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 { diff --git a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java index 225cfc9097..e656dc1d88 100644 --- a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java +++ b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java @@ -34,13 +34,10 @@ import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExcep import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.msg.tools.TbRateLimitsException; -import org.thingsboard.server.dao.exception.DataValidationException; -import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.service.security.exception.AuthMethodNotSupportedException; import org.thingsboard.server.service.security.exception.JwtExpiredTokenException; import org.thingsboard.server.service.security.exception.UserPasswordExpiredException; -import javax.mail.MessagingException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -74,17 +71,6 @@ public class ThingsboardErrorResponseHandler extends ResponseEntityExceptionHand try { response.setContentType(MediaType.APPLICATION_JSON_VALUE); - String cause = ""; - if (exception.getCause() != null) { - cause = exception.getCause().getClass().getCanonicalName(); - } - if (exception instanceof IllegalArgumentException || exception instanceof IncorrectParameterException - || exception instanceof DataValidationException || cause.contains("IncorrectParameterException")) { - exception = new ThingsboardException(exception.getMessage(), ThingsboardErrorCode.BAD_REQUEST_PARAMS); - } else if (exception instanceof MessagingException) { - exception = new ThingsboardException("Unable to send mail: " + exception.getMessage(), ThingsboardErrorCode.GENERAL); - } - if (exception instanceof ThingsboardException) { ThingsboardException thingsboardException = (ThingsboardException) exception; if (thingsboardException.getErrorCode() == ThingsboardErrorCode.SUBSCRIPTION_VIOLATION) { 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 468b409c3e..59fad42fc7 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 @@ -35,6 +35,7 @@ public class WidgetsBundle extends SearchTextBased implements H private String title; private String image; @NoXss + @Length(fieldName = "description") private String description; public WidgetsBundle() { From 30c43812cb1abe68faff93fb94e96eba1df6c6f6 Mon Sep 17 00:00:00 2001 From: Swoq Date: Thu, 2 Sep 2021 17:23:58 +0300 Subject: [PATCH 10/11] Test typo fix --- .../thingsboard/server/controller/BaseUserControllerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 7e378ea2dd..e25b5e35ad 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseUserControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseUserControllerTest.java @@ -121,7 +121,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest { user.setEmail(email); user.setFirstName(RandomStringUtils.randomAlphabetic(300)); user.setLastName("Downs"); - doPost("/api/user", user).andExpect(statusReason(containsString("Validation error: length of firs name must be equal or less than 255"))); + 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"))); From fe5215c7ccb6df14ae34fe81a07c4f61c164b8ce Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Thu, 4 Nov 2021 12:55:23 +0200 Subject: [PATCH 11/11] Minor refactoring --- .../java/org/thingsboard/server/controller/AdminController.java | 1 - .../thingsboard/server/controller/EntityRelationController.java | 2 -- .../server/exception/ThingsboardErrorResponseHandler.java | 1 + .../thingsboard/server/dao/relation/BaseRelationService.java | 2 ++ 4 files changed, 3 insertions(+), 3 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/AdminController.java b/application/src/main/java/org/thingsboard/server/controller/AdminController.java index 1fbd52d8e3..d9a8f436b9 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AdminController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AdminController.java @@ -103,7 +103,6 @@ public class AdminController extends BaseController { } else if (adminSettings.getKey().equals("sms")) { smsService.updateSmsConfiguration(); } - return adminSettings; } catch (Exception e) { throw handleException(e); diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java b/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java index 7131fc0edf..f5c5bdd284 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java @@ -37,7 +37,6 @@ import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntityRelationInfo; import org.thingsboard.server.common.data.relation.EntityRelationsQuery; import org.thingsboard.server.common.data.relation.RelationTypeGroup; -import org.thingsboard.server.dao.service.ConstraintValidator; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.permission.Operation; @@ -85,7 +84,6 @@ public class EntityRelationController extends BaseController { if (relation.getTypeGroup() == null) { relation.setTypeGroup(RelationTypeGroup.COMMON); } - ConstraintValidator.validateFields(relation); relationService.saveRelation(getTenantId(), relation); logEntityAction(relation.getFrom(), null, getCurrentUser().getCustomerId(), diff --git a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java index e656dc1d88..906e87d608 100644 --- a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java +++ b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java @@ -95,6 +95,7 @@ public class ThingsboardErrorResponseHandler extends ResponseEntityExceptionHand } } + private void handleThingsboardException(ThingsboardException thingsboardException, HttpServletResponse response) throws IOException { ThingsboardErrorCode errorCode = thingsboardException.getErrorCode(); 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()); }