NoXss and length validation for notification entities
This commit is contained in:
		
							parent
							
								
									5a57657479
								
							
						
					
					
						commit
						e926854911
					
				@ -103,7 +103,6 @@ import org.thingsboard.server.common.data.rpc.Rpc;
 | 
			
		||||
import org.thingsboard.server.common.data.rule.RuleChain;
 | 
			
		||||
import org.thingsboard.server.common.data.rule.RuleChainType;
 | 
			
		||||
import org.thingsboard.server.common.data.rule.RuleNode;
 | 
			
		||||
import org.thingsboard.server.common.data.settings.UserDashboardAction;
 | 
			
		||||
import org.thingsboard.server.common.data.util.ThrowingBiFunction;
 | 
			
		||||
import org.thingsboard.server.common.data.widget.WidgetTypeDetails;
 | 
			
		||||
import org.thingsboard.server.common.data.widget.WidgetsBundle;
 | 
			
		||||
@ -130,12 +129,12 @@ import org.thingsboard.server.dao.queue.QueueService;
 | 
			
		||||
import org.thingsboard.server.dao.relation.RelationService;
 | 
			
		||||
import org.thingsboard.server.dao.rpc.RpcService;
 | 
			
		||||
import org.thingsboard.server.dao.rule.RuleChainService;
 | 
			
		||||
import org.thingsboard.server.dao.service.ConstraintValidator;
 | 
			
		||||
import org.thingsboard.server.dao.service.Validator;
 | 
			
		||||
import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
 | 
			
		||||
import org.thingsboard.server.dao.tenant.TenantProfileService;
 | 
			
		||||
import org.thingsboard.server.dao.tenant.TenantService;
 | 
			
		||||
import org.thingsboard.server.dao.user.UserService;
 | 
			
		||||
import org.thingsboard.server.dao.user.UserSettingsService;
 | 
			
		||||
import org.thingsboard.server.dao.widget.WidgetTypeService;
 | 
			
		||||
import org.thingsboard.server.dao.widget.WidgetsBundleService;
 | 
			
		||||
import org.thingsboard.server.exception.ThingsboardErrorResponseHandler;
 | 
			
		||||
@ -164,7 +163,9 @@ import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
 | 
			
		||||
 | 
			
		||||
import javax.mail.MessagingException;
 | 
			
		||||
import javax.servlet.http.HttpServletResponse;
 | 
			
		||||
import javax.validation.ConstraintViolation;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
import java.util.Optional;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
@ -395,16 +396,19 @@ public abstract class BaseController {
 | 
			
		||||
     * Handles validation error for controller method arguments annotated with @{@link javax.validation.Valid}
 | 
			
		||||
     * */
 | 
			
		||||
    @ExceptionHandler(MethodArgumentNotValidException.class)
 | 
			
		||||
    public void handleValidationError(MethodArgumentNotValidException e, HttpServletResponse response) {
 | 
			
		||||
        String errorMessage = "Validation error: " + e.getFieldErrors().stream()
 | 
			
		||||
    public void handleValidationError(MethodArgumentNotValidException validationError, HttpServletResponse response) {
 | 
			
		||||
        List<ConstraintViolation<Object>> constraintsViolations = validationError.getFieldErrors().stream()
 | 
			
		||||
                .map(fieldError -> {
 | 
			
		||||
                    String property = fieldError.getField();
 | 
			
		||||
                    if (property.equals("valid") || StringUtils.endsWith(property, ".valid")) { // when custom @AssertTrue is used
 | 
			
		||||
                        property = "";
 | 
			
		||||
                    try {
 | 
			
		||||
                        return (ConstraintViolation<Object>) fieldError.unwrap(ConstraintViolation.class);
 | 
			
		||||
                    } catch (Exception e) {
 | 
			
		||||
                        log.warn("FieldError source is not of type ConstraintViolation");
 | 
			
		||||
                        return null; // should not happen
 | 
			
		||||
                    }
 | 
			
		||||
                    return (!property.isEmpty() ? (property + " ") : "") + fieldError.getDefaultMessage();
 | 
			
		||||
                })
 | 
			
		||||
                .collect(Collectors.joining(", "));
 | 
			
		||||
                .filter(Objects::nonNull)
 | 
			
		||||
                .collect(Collectors.toList());
 | 
			
		||||
        String errorMessage = "Validation error: " + ConstraintValidator.getErrorMessage(constraintsViolations);
 | 
			
		||||
        ThingsboardException thingsboardException = new ThingsboardException(errorMessage, ThingsboardErrorCode.BAD_REQUEST_PARAMS);
 | 
			
		||||
        handleControllerException(thingsboardException, response);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -125,8 +125,10 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
 | 
			
		||||
        NotificationRuleId ruleId = request.getRuleId();
 | 
			
		||||
        notificationTemplate.getConfiguration().getDeliveryMethodsTemplates().forEach((deliveryMethod, template) -> {
 | 
			
		||||
            if (!template.isEnabled()) return;
 | 
			
		||||
            if (!channels.get(deliveryMethod).check(tenantId)) {
 | 
			
		||||
                throw new IllegalArgumentException("Unable to send notification via " + deliveryMethod.getName() + ": not configured or not working");
 | 
			
		||||
            try {
 | 
			
		||||
                channels.get(deliveryMethod).check(tenantId);
 | 
			
		||||
            } catch (Exception e) {
 | 
			
		||||
                throw new IllegalArgumentException(e.getMessage());
 | 
			
		||||
            }
 | 
			
		||||
            if (ruleId == null) {
 | 
			
		||||
                if (targets.stream().noneMatch(target -> target.getConfiguration().getType().getSupportedDeliveryMethods().contains(deliveryMethod))) {
 | 
			
		||||
@ -341,14 +343,20 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
 | 
			
		||||
    @Override
 | 
			
		||||
    public Set<NotificationDeliveryMethod> getAvailableDeliveryMethods(TenantId tenantId) {
 | 
			
		||||
        return channels.values().stream()
 | 
			
		||||
                .filter(channel -> channel.check(tenantId))
 | 
			
		||||
                .filter(channel -> {
 | 
			
		||||
                    try {
 | 
			
		||||
                        channel.check(tenantId);
 | 
			
		||||
                        return true;
 | 
			
		||||
                    } catch (Exception e) {
 | 
			
		||||
                        return false;
 | 
			
		||||
                    }
 | 
			
		||||
                })
 | 
			
		||||
                .map(NotificationChannel::getDeliveryMethod)
 | 
			
		||||
                .collect(Collectors.toSet());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean check(TenantId tenantId) {
 | 
			
		||||
        return true;
 | 
			
		||||
    public void check(TenantId tenantId) throws Exception {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
 | 
			
		||||
@ -48,12 +48,11 @@ public class EmailNotificationChannel implements NotificationChannel<User, Email
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean check(TenantId tenantId) {
 | 
			
		||||
    public void check(TenantId tenantId) throws Exception {
 | 
			
		||||
        try {
 | 
			
		||||
            mailService.testConnection(tenantId);
 | 
			
		||||
            return true;
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            return false;
 | 
			
		||||
            throw new RuntimeException("Mail server is not available");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -26,7 +26,7 @@ public interface NotificationChannel<R extends NotificationRecipient, T extends
 | 
			
		||||
 | 
			
		||||
    ListenableFuture<Void> sendNotification(R recipient, T processedTemplate, NotificationProcessingContext ctx);
 | 
			
		||||
 | 
			
		||||
    boolean check(TenantId tenantId);
 | 
			
		||||
    void check(TenantId tenantId) throws Exception;
 | 
			
		||||
 | 
			
		||||
    NotificationDeliveryMethod getDeliveryMethod();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -22,12 +22,12 @@ import org.thingsboard.rule.engine.api.slack.SlackService;
 | 
			
		||||
import org.thingsboard.server.common.data.id.TenantId;
 | 
			
		||||
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
 | 
			
		||||
import org.thingsboard.server.common.data.notification.settings.NotificationSettings;
 | 
			
		||||
import org.thingsboard.server.dao.notification.NotificationSettingsService;
 | 
			
		||||
import org.thingsboard.server.service.notification.NotificationProcessingContext;
 | 
			
		||||
import org.thingsboard.server.common.data.notification.settings.SlackNotificationDeliveryMethodConfig;
 | 
			
		||||
import org.thingsboard.server.common.data.notification.targets.slack.SlackConversation;
 | 
			
		||||
import org.thingsboard.server.common.data.notification.template.SlackDeliveryMethodNotificationTemplate;
 | 
			
		||||
import org.thingsboard.server.dao.notification.NotificationSettingsService;
 | 
			
		||||
import org.thingsboard.server.service.executors.ExternalCallExecutorService;
 | 
			
		||||
import org.thingsboard.server.service.notification.NotificationProcessingContext;
 | 
			
		||||
 | 
			
		||||
@Component
 | 
			
		||||
@RequiredArgsConstructor
 | 
			
		||||
@ -47,9 +47,11 @@ public class SlackNotificationChannel implements NotificationChannel<SlackConver
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean check(TenantId tenantId) {
 | 
			
		||||
    public void check(TenantId tenantId) throws Exception {
 | 
			
		||||
        NotificationSettings notificationSettings = notificationSettingsService.findNotificationSettings(tenantId);
 | 
			
		||||
        return notificationSettings.getDeliveryMethodsConfigs().containsKey(NotificationDeliveryMethod.SLACK);
 | 
			
		||||
        if (!notificationSettings.getDeliveryMethodsConfigs().containsKey(NotificationDeliveryMethod.SLACK)) {
 | 
			
		||||
            throw new RuntimeException("Slack API token is not configured");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
 | 
			
		||||
@ -49,8 +49,10 @@ public class SmsNotificationChannel implements NotificationChannel<User, SmsDeli
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean check(TenantId tenantId) {
 | 
			
		||||
        return smsService.isConfigured(tenantId);
 | 
			
		||||
    public void check(TenantId tenantId) throws Exception {
 | 
			
		||||
        if (!smsService.isConfigured(tenantId)) {
 | 
			
		||||
            throw new RuntimeException("SMS provider is not configured");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
 | 
			
		||||
@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.id.NotificationTemplateId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.TenantId;
 | 
			
		||||
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerConfig;
 | 
			
		||||
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType;
 | 
			
		||||
import org.thingsboard.server.common.data.validation.Length;
 | 
			
		||||
import org.thingsboard.server.common.data.validation.NoXss;
 | 
			
		||||
 | 
			
		||||
import javax.validation.Valid;
 | 
			
		||||
@ -43,6 +44,7 @@ public class NotificationRule extends BaseData<NotificationRuleId> implements Ha
 | 
			
		||||
    private TenantId tenantId;
 | 
			
		||||
    @NotBlank
 | 
			
		||||
    @NoXss
 | 
			
		||||
    @Length(max = 255, message = "cannot be longer than 255 chars")
 | 
			
		||||
    private String name;
 | 
			
		||||
    @NotNull
 | 
			
		||||
    private NotificationTemplateId templateId;
 | 
			
		||||
 | 
			
		||||
@ -22,6 +22,7 @@ import org.thingsboard.server.common.data.HasName;
 | 
			
		||||
import org.thingsboard.server.common.data.HasTenantId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.NotificationTargetId;
 | 
			
		||||
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;
 | 
			
		||||
@ -35,6 +36,7 @@ public class NotificationTarget extends BaseData<NotificationTargetId> implement
 | 
			
		||||
    private TenantId tenantId;
 | 
			
		||||
    @NotBlank
 | 
			
		||||
    @NoXss
 | 
			
		||||
    @Length(max = 255, message = "cannot be longer than 255 chars")
 | 
			
		||||
    private String name;
 | 
			
		||||
    @NotNull
 | 
			
		||||
    @Valid
 | 
			
		||||
 | 
			
		||||
@ -23,6 +23,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import org.thingsboard.server.common.data.notification.targets.platform.PlatformUsersNotificationTargetConfig;
 | 
			
		||||
import org.thingsboard.server.common.data.notification.targets.slack.SlackNotificationTargetConfig;
 | 
			
		||||
import org.thingsboard.server.common.data.validation.Length;
 | 
			
		||||
import org.thingsboard.server.common.data.validation.NoXss;
 | 
			
		||||
 | 
			
		||||
@JsonIgnoreProperties(ignoreUnknown = true)
 | 
			
		||||
@ -35,6 +36,7 @@ import org.thingsboard.server.common.data.validation.NoXss;
 | 
			
		||||
public abstract class NotificationTargetConfig {
 | 
			
		||||
 | 
			
		||||
    @NoXss
 | 
			
		||||
    @Length(max = 500, message = "cannot be longer than 500 chars")
 | 
			
		||||
    private String description;
 | 
			
		||||
 | 
			
		||||
    @JsonIgnore
 | 
			
		||||
 | 
			
		||||
@ -21,6 +21,8 @@ import lombok.NoArgsConstructor;
 | 
			
		||||
import lombok.ToString;
 | 
			
		||||
import org.apache.commons.lang3.StringUtils;
 | 
			
		||||
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
 | 
			
		||||
import org.thingsboard.server.common.data.validation.Length;
 | 
			
		||||
import org.thingsboard.server.common.data.validation.NoXss;
 | 
			
		||||
 | 
			
		||||
import javax.validation.constraints.NotEmpty;
 | 
			
		||||
 | 
			
		||||
@ -30,6 +32,8 @@ import javax.validation.constraints.NotEmpty;
 | 
			
		||||
@ToString(callSuper = true)
 | 
			
		||||
public class EmailDeliveryMethodNotificationTemplate extends DeliveryMethodNotificationTemplate implements HasSubject {
 | 
			
		||||
 | 
			
		||||
    @NoXss(fieldName = "email subject")
 | 
			
		||||
    @Length(fieldName = "email subject", max = 250, message = "cannot be longer than 250 chars")
 | 
			
		||||
    @NotEmpty
 | 
			
		||||
    private String subject;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.HasTenantId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.NotificationTemplateId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.TenantId;
 | 
			
		||||
import org.thingsboard.server.common.data.notification.NotificationType;
 | 
			
		||||
import org.thingsboard.server.common.data.validation.Length;
 | 
			
		||||
import org.thingsboard.server.common.data.validation.NoXss;
 | 
			
		||||
 | 
			
		||||
import javax.validation.Valid;
 | 
			
		||||
@ -36,6 +37,7 @@ public class NotificationTemplate extends BaseData<NotificationTemplateId> imple
 | 
			
		||||
    private TenantId tenantId;
 | 
			
		||||
    @NoXss
 | 
			
		||||
    @NotEmpty
 | 
			
		||||
    @Length(max = 255, message = "cannot be longer than 255 chars")
 | 
			
		||||
    private String name;
 | 
			
		||||
    @NoXss
 | 
			
		||||
    @NotNull
 | 
			
		||||
 | 
			
		||||
@ -1,30 +0,0 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright © 2016-2023 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.notification.template;
 | 
			
		||||
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
 | 
			
		||||
@Data
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
@NoArgsConstructor
 | 
			
		||||
public class NotificationText {
 | 
			
		||||
 | 
			
		||||
    private String body;
 | 
			
		||||
    private String subject;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -20,6 +20,7 @@ import lombok.EqualsAndHashCode;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
import lombok.ToString;
 | 
			
		||||
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
 | 
			
		||||
import org.thingsboard.server.common.data.validation.NoXss;
 | 
			
		||||
 | 
			
		||||
@Data
 | 
			
		||||
@NoArgsConstructor
 | 
			
		||||
@ -31,6 +32,12 @@ public class SlackDeliveryMethodNotificationTemplate extends DeliveryMethodNotif
 | 
			
		||||
        super(other);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @NoXss(fieldName = "Slack message")
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getBody() {
 | 
			
		||||
        return super.getBody();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public NotificationDeliveryMethod getMethod() {
 | 
			
		||||
        return NotificationDeliveryMethod.SLACK;
 | 
			
		||||
 | 
			
		||||
@ -20,6 +20,8 @@ import lombok.EqualsAndHashCode;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
import lombok.ToString;
 | 
			
		||||
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
 | 
			
		||||
import org.thingsboard.server.common.data.validation.Length;
 | 
			
		||||
import org.thingsboard.server.common.data.validation.NoXss;
 | 
			
		||||
 | 
			
		||||
@Data
 | 
			
		||||
@NoArgsConstructor
 | 
			
		||||
@ -31,6 +33,13 @@ public class SmsDeliveryMethodNotificationTemplate extends DeliveryMethodNotific
 | 
			
		||||
        super(other);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @NoXss(fieldName = "SMS message")
 | 
			
		||||
    @Length(fieldName = "SMS message", max = 320, message = "cannot be longer than 320 chars")
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getBody() {
 | 
			
		||||
        return super.getBody();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public NotificationDeliveryMethod getMethod() {
 | 
			
		||||
        return NotificationDeliveryMethod.SMS;
 | 
			
		||||
 | 
			
		||||
@ -25,6 +25,8 @@ import lombok.NoArgsConstructor;
 | 
			
		||||
import lombok.ToString;
 | 
			
		||||
import org.apache.commons.lang3.StringUtils;
 | 
			
		||||
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
 | 
			
		||||
import org.thingsboard.server.common.data.validation.Length;
 | 
			
		||||
import org.thingsboard.server.common.data.validation.NoXss;
 | 
			
		||||
 | 
			
		||||
import javax.validation.constraints.NotEmpty;
 | 
			
		||||
import java.util.Optional;
 | 
			
		||||
@ -35,6 +37,8 @@ import java.util.Optional;
 | 
			
		||||
@ToString(callSuper = true)
 | 
			
		||||
public class WebDeliveryMethodNotificationTemplate extends DeliveryMethodNotificationTemplate implements HasSubject {
 | 
			
		||||
 | 
			
		||||
    @NoXss(fieldName = "web notification subject")
 | 
			
		||||
    @Length(fieldName = "web notification subject", max = 150, message = "cannot be longer than 150 chars")
 | 
			
		||||
    @NotEmpty
 | 
			
		||||
    private String subject;
 | 
			
		||||
    private JsonNode additionalConfig;
 | 
			
		||||
@ -45,6 +49,15 @@ public class WebDeliveryMethodNotificationTemplate extends DeliveryMethodNotific
 | 
			
		||||
        this.additionalConfig = other.additionalConfig != null ? other.additionalConfig.deepCopy() : null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @NoXss(fieldName = "web notification message")
 | 
			
		||||
    @Length(fieldName = "web notification message", max = 250, message = "cannot be longer than 250 chars")
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getBody() {
 | 
			
		||||
        return super.getBody();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @NoXss(fieldName = "web notification button text")
 | 
			
		||||
    @Length(fieldName = "web notification button text", max = 50, message = "cannot be longer than 50 chars")
 | 
			
		||||
    @JsonIgnore
 | 
			
		||||
    public String getButtonText() {
 | 
			
		||||
        return getButtonConfigProperty("text");
 | 
			
		||||
@ -57,6 +70,8 @@ public class WebDeliveryMethodNotificationTemplate extends DeliveryMethodNotific
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @NoXss(fieldName = "web notification button link")
 | 
			
		||||
    @Length(fieldName = "web notification button link", max = 300, message = "cannot be longer than 300 chars")
 | 
			
		||||
    @JsonIgnore
 | 
			
		||||
    public String getButtonLink() {
 | 
			
		||||
        return getButtonConfigProperty("link");
 | 
			
		||||
 | 
			
		||||
@ -23,12 +23,12 @@ import java.lang.annotation.RetentionPolicy;
 | 
			
		||||
import java.lang.annotation.Target;
 | 
			
		||||
 | 
			
		||||
@Retention(RetentionPolicy.RUNTIME)
 | 
			
		||||
@Target(ElementType.FIELD)
 | 
			
		||||
@Target({ElementType.FIELD, ElementType.METHOD})
 | 
			
		||||
@Constraint(validatedBy = {})
 | 
			
		||||
public @interface Length {
 | 
			
		||||
    String message() default "length must be equal or less than {max}";
 | 
			
		||||
 | 
			
		||||
    String fieldName();
 | 
			
		||||
    String fieldName() default "";
 | 
			
		||||
 | 
			
		||||
    int max() default 255;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -23,12 +23,16 @@ import java.lang.annotation.RetentionPolicy;
 | 
			
		||||
import java.lang.annotation.Target;
 | 
			
		||||
 | 
			
		||||
@Retention(RetentionPolicy.RUNTIME)
 | 
			
		||||
@Target(ElementType.FIELD)
 | 
			
		||||
@Target({ElementType.FIELD, ElementType.METHOD})
 | 
			
		||||
@Constraint(validatedBy = {})
 | 
			
		||||
public @interface NoXss {
 | 
			
		||||
 | 
			
		||||
    String message() default "is malformed";
 | 
			
		||||
 | 
			
		||||
    String fieldName() default "";
 | 
			
		||||
 | 
			
		||||
    Class<?>[] groups() default {};
 | 
			
		||||
 | 
			
		||||
    Class<? extends Payload>[] payload() default {};
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -17,6 +17,7 @@ package org.thingsboard.server.dao.service;
 | 
			
		||||
 | 
			
		||||
import com.google.common.collect.Iterators;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.apache.commons.lang3.StringUtils;
 | 
			
		||||
import org.hibernate.validator.HibernateValidator;
 | 
			
		||||
import org.hibernate.validator.HibernateValidatorConfiguration;
 | 
			
		||||
import org.hibernate.validator.cfg.ConstraintMapping;
 | 
			
		||||
@ -29,11 +30,13 @@ import org.thingsboard.server.common.data.validation.Length;
 | 
			
		||||
import org.thingsboard.server.common.data.validation.NoXss;
 | 
			
		||||
import org.thingsboard.server.dao.exception.DataValidationException;
 | 
			
		||||
 | 
			
		||||
import javax.validation.Path;
 | 
			
		||||
import javax.validation.ConstraintViolation;
 | 
			
		||||
import javax.validation.Validation;
 | 
			
		||||
import javax.validation.Validator;
 | 
			
		||||
import javax.validation.constraints.AssertTrue;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import javax.validation.metadata.ConstraintDescriptor;
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
 | 
			
		||||
@Slf4j
 | 
			
		||||
@ -51,26 +54,31 @@ public class ConstraintValidator {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void validateFields(Object data, String errorPrefix) {
 | 
			
		||||
        List<String> constraintsViolations = getConstraintsViolations(data);
 | 
			
		||||
        Set<ConstraintViolation<Object>> constraintsViolations = fieldsValidator.validate(data);
 | 
			
		||||
        if (!constraintsViolations.isEmpty()) {
 | 
			
		||||
            throw new DataValidationException(errorPrefix + String.join(", ", constraintsViolations));
 | 
			
		||||
            throw new DataValidationException(errorPrefix + getErrorMessage(constraintsViolations));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static List<String> getConstraintsViolations(Object data) {
 | 
			
		||||
        return fieldsValidator.validate(data).stream()
 | 
			
		||||
                .map(constraintViolation -> {
 | 
			
		||||
                    String property;
 | 
			
		||||
                    if (constraintViolation.getConstraintDescriptor().getAttributes().containsKey("fieldName")) {
 | 
			
		||||
                        property = constraintViolation.getConstraintDescriptor().getAttributes().get("fieldName").toString();
 | 
			
		||||
                    } else {
 | 
			
		||||
                        Path propertyPath = constraintViolation.getPropertyPath();
 | 
			
		||||
                        property = Iterators.getLast(propertyPath.iterator()).toString();
 | 
			
		||||
    public static String getErrorMessage(Collection<ConstraintViolation<Object>> constraintsViolations) {
 | 
			
		||||
        return constraintsViolations.stream()
 | 
			
		||||
                .map(ConstraintValidator::getErrorMessage)
 | 
			
		||||
                .distinct().sorted().collect(Collectors.joining(", "));
 | 
			
		||||
    }
 | 
			
		||||
                    return property + " " + constraintViolation.getMessage();
 | 
			
		||||
                })
 | 
			
		||||
                .distinct()
 | 
			
		||||
                .collect(Collectors.toList());
 | 
			
		||||
 | 
			
		||||
    public static String getErrorMessage(ConstraintViolation<Object> constraintViolation) {
 | 
			
		||||
        ConstraintDescriptor<?> constraintDescriptor = constraintViolation.getConstraintDescriptor();
 | 
			
		||||
        String property = (String) constraintDescriptor.getAttributes().get("fieldName");
 | 
			
		||||
        if (StringUtils.isEmpty(property) && !(constraintDescriptor.getAnnotation() instanceof AssertTrue)) {
 | 
			
		||||
            property = Iterators.getLast(constraintViolation.getPropertyPath().iterator()).toString();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        String error = "";
 | 
			
		||||
        if (StringUtils.isNotEmpty(property)) {
 | 
			
		||||
            error += property + " ";
 | 
			
		||||
        }
 | 
			
		||||
        error += constraintViolation.getMessage();
 | 
			
		||||
        return error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void initializeValidators() {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user