From 41e2eaea44328e1892c3481d092bda0f55a8b207 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Tue, 20 Nov 2018 15:22:00 +0200 Subject: [PATCH 01/19] Migrate to Spring Boot 2.1.0 and Spring 5.1.2 --- application/pom.xml | 2 +- .../config/AuditLogLevelProperties.java | 2 +- .../config/SchedulingConfiguration.java | 47 +++++++ .../ThingsboardMessageConfiguration.java | 128 +++++++++++++++++- .../ThingsboardSecurityConfiguration.java | 2 +- .../ThingsboardWebFluxSecurityConfig.java | 61 +++++++++ .../server/config/WebSocketConfiguration.java | 47 +++++-- .../controller/plugin/TbWebSocketHandler.java | 50 +++++-- ...assandraAbstractDatabaseSchemaService.java | 2 + .../CassandraDatabaseUpgradeService.java | 2 + .../service/mail/DefaultMailService.java | 32 ++++- .../webflux/WebfluxAuthenticationManager.java | 48 +++++++ .../JwtTokenSecurityContextRepository.java | 86 ++++++++++++ .../DefaultTelemetryWebSocketService.java | 3 +- .../TelemetryWebSocketMsgEndpoint.java | 2 +- .../src/main/resources/thingsboard.yml | 8 +- ...tractMqttServerSideRpcIntegrationTest.java | 4 +- .../org/thingsboard/server/dao/DaoUtil.java | 14 +- .../server/dao/audit/AuditLogServiceImpl.java | 2 +- .../dao/audit/CassandraAuditLogDao.java | 4 +- .../dao/audit/DummyAuditLogServiceImpl.java | 2 +- .../dao/audit/sink/DummyAuditLogSink.java | 2 +- .../audit/sink/ElasticsearchAuditLogSink.java | 16 +-- .../dao/cache/TBRedisCacheConfiguration.java | 43 ++++-- .../dao/cassandra/CassandraCluster.java | 2 +- .../cassandra/CassandraInstallCluster.java | 2 +- .../dao/nosql/CassandraAbstractDao.java | 2 + .../server/dao/sql/JpaAbstractDao.java | 9 +- .../dao/sql/attributes/JpaAttributeDao.java | 6 +- .../JpaBaseComponentDescriptorDao.java | 2 +- .../dao/sql/relation/JpaRelationDao.java | 8 +- .../dao/sql/relation/RelationRepository.java | 2 +- .../dao/sql/timeseries/JpaTimeseriesDao.java | 6 +- .../resources/application-test.properties | 8 +- dao/src/test/resources/sql-test.properties | 1 + docker/docker-compose.yml | 3 + pom.xml | 48 ++++--- 37 files changed, 586 insertions(+), 122 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/config/SchedulingConfiguration.java create mode 100644 application/src/main/java/org/thingsboard/server/config/ThingsboardWebFluxSecurityConfig.java create mode 100644 application/src/main/java/org/thingsboard/server/service/security/auth/webflux/WebfluxAuthenticationManager.java create mode 100644 application/src/main/java/org/thingsboard/server/service/security/auth/webflux/jwt/JwtTokenSecurityContextRepository.java diff --git a/application/pom.xml b/application/pom.xml index d99f60773e..238c5f47f9 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -122,7 +122,7 @@ org.springframework.boot - spring-boot-starter-websocket + spring-boot-starter-webflux io.jsonwebtoken diff --git a/application/src/main/java/org/thingsboard/server/config/AuditLogLevelProperties.java b/application/src/main/java/org/thingsboard/server/config/AuditLogLevelProperties.java index 4c36a15758..d05f8326b1 100644 --- a/application/src/main/java/org/thingsboard/server/config/AuditLogLevelProperties.java +++ b/application/src/main/java/org/thingsboard/server/config/AuditLogLevelProperties.java @@ -22,7 +22,7 @@ import java.util.HashMap; import java.util.Map; @Configuration -@ConfigurationProperties(prefix = "audit_log.logging_level") +@ConfigurationProperties(prefix = "audit-log.logging-level") public class AuditLogLevelProperties { private Map mask = new HashMap<>(); diff --git a/application/src/main/java/org/thingsboard/server/config/SchedulingConfiguration.java b/application/src/main/java/org/thingsboard/server/config/SchedulingConfiguration.java new file mode 100644 index 0000000000..aebd63698a --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/config/SchedulingConfiguration.java @@ -0,0 +1,47 @@ +/** + * Copyright © 2016-2018 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.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.TaskScheduler; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.SchedulingConfigurer; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; +import org.springframework.scheduling.config.ScheduledTaskRegistrar; + +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +@Configuration +@EnableScheduling +public class SchedulingConfiguration implements SchedulingConfigurer { + + @Override + public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { + taskRegistrar.setScheduler(taskScheduler()); + } + + @Bean(destroyMethod="shutdown") + public TaskScheduler taskScheduler() { + ThreadPoolTaskScheduler threadPoolScheduler = new ThreadPoolTaskScheduler(); + threadPoolScheduler.setThreadNamePrefix("TB-Scheduling-"); + threadPoolScheduler.setPoolSize(Runtime.getRuntime().availableProcessors()); + threadPoolScheduler.setRemoveOnCancelPolicy(true); + return threadPoolScheduler; + } +} diff --git a/application/src/main/java/org/thingsboard/server/config/ThingsboardMessageConfiguration.java b/application/src/main/java/org/thingsboard/server/config/ThingsboardMessageConfiguration.java index 038f8bc6ae..a44d9fedcb 100644 --- a/application/src/main/java/org/thingsboard/server/config/ThingsboardMessageConfiguration.java +++ b/application/src/main/java/org/thingsboard/server/config/ThingsboardMessageConfiguration.java @@ -15,11 +15,28 @@ */ package org.thingsboard.server.config; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.ExtendedProperties; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.velocity.app.VelocityEngine; +import org.apache.velocity.exception.ResourceNotFoundException; +import org.apache.velocity.runtime.RuntimeConstants; +import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader; import org.springframework.context.MessageSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.context.support.ResourceBundleMessageSource; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.springframework.util.StringUtils; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; @Configuration public class ThingsboardMessageConfiguration { @@ -32,5 +49,114 @@ public class ThingsboardMessageConfiguration { messageSource.setDefaultEncoding("UTF-8"); return messageSource; } - + + private static final String DEFAULT_RESOURCE_LOADER_PATH = "classpath:/templates/"; + + private ResourceLoader resourceLoader = new DefaultResourceLoader(); + + @Bean + public VelocityEngine velocityEngine() { + VelocityEngine velocityEngine = new VelocityEngine(); + try { + Resource resource = resourceLoader.getResource(DEFAULT_RESOURCE_LOADER_PATH); + File file = resource.getFile(); + velocityEngine.setProperty(RuntimeConstants.RESOURCE_LOADER, "file"); + velocityEngine.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_CACHE, "true"); + velocityEngine.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, file.getAbsolutePath()); + } catch (IOException e) { + initSpringResourceLoader(velocityEngine, DEFAULT_RESOURCE_LOADER_PATH); + } + velocityEngine.init(); + return velocityEngine; + } + + private void initSpringResourceLoader(VelocityEngine velocityEngine, String resourceLoaderPath) { + velocityEngine.setProperty( + RuntimeConstants.RESOURCE_LOADER, SpringResourceLoader.NAME); + velocityEngine.setProperty( + SpringResourceLoader.SPRING_RESOURCE_LOADER_CLASS, SpringResourceLoader.class.getName()); + velocityEngine.setProperty( + SpringResourceLoader.SPRING_RESOURCE_LOADER_CACHE, "true"); + velocityEngine.setApplicationAttribute( + SpringResourceLoader.SPRING_RESOURCE_LOADER, resourceLoader); + velocityEngine.setApplicationAttribute( + SpringResourceLoader.SPRING_RESOURCE_LOADER_PATH, resourceLoaderPath); + } + + @Slf4j + static class SpringResourceLoader extends org.apache.velocity.runtime.resource.loader.ResourceLoader { + + public static final String NAME = "spring"; + + public static final String SPRING_RESOURCE_LOADER_CLASS = "spring.resource.loader.class"; + + public static final String SPRING_RESOURCE_LOADER_CACHE = "spring.resource.loader.cache"; + + public static final String SPRING_RESOURCE_LOADER = "spring.resource.loader"; + + public static final String SPRING_RESOURCE_LOADER_PATH = "spring.resource.loader.path"; + + private org.springframework.core.io.ResourceLoader resourceLoader; + + private String[] resourceLoaderPaths; + + + @Override + public void init(ExtendedProperties configuration) { + this.resourceLoader = (org.springframework.core.io.ResourceLoader) + this.rsvc.getApplicationAttribute(SPRING_RESOURCE_LOADER); + String resourceLoaderPath = (String) this.rsvc.getApplicationAttribute(SPRING_RESOURCE_LOADER_PATH); + if (this.resourceLoader == null) { + throw new IllegalArgumentException( + "'resourceLoader' application attribute must be present for SpringResourceLoader"); + } + if (resourceLoaderPath == null) { + throw new IllegalArgumentException( + "'resourceLoaderPath' application attribute must be present for SpringResourceLoader"); + } + this.resourceLoaderPaths = StringUtils.commaDelimitedListToStringArray(resourceLoaderPath); + for (int i = 0; i < this.resourceLoaderPaths.length; i++) { + String path = this.resourceLoaderPaths[i]; + if (!path.endsWith("/")) { + this.resourceLoaderPaths[i] = path + "/"; + } + } + if (log.isInfoEnabled()) { + log.info("SpringResourceLoader for Velocity: using resource loader [" + this.resourceLoader + + "] and resource loader paths " + Arrays.asList(this.resourceLoaderPaths)); + } + } + + @Override + public InputStream getResourceStream(String source) throws ResourceNotFoundException { + if (log.isDebugEnabled()) { + log.debug("Looking for Velocity resource with name [" + source + "]"); + } + for (String resourceLoaderPath : this.resourceLoaderPaths) { + org.springframework.core.io.Resource resource = + this.resourceLoader.getResource(resourceLoaderPath + source); + try { + return resource.getInputStream(); + } + catch (IOException ex) { + if (log.isDebugEnabled()) { + log.debug("Could not find Velocity resource: " + resource); + } + } + } + throw new ResourceNotFoundException( + "Could not find resource [" + source + "] in Spring resource loader path"); + } + + @Override + public boolean isSourceModified(org.apache.velocity.runtime.resource.Resource resource) { + return false; + } + + @Override + public long getLastModified(org.apache.velocity.runtime.resource.Resource resource) { + return 0; + } + + } } diff --git a/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java b/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java index 1901e49c3d..13f7c569c4 100644 --- a/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java +++ b/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java @@ -57,7 +57,7 @@ import java.util.List; @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled=true) -@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER) +@Order(SecurityProperties.BASIC_AUTH_ORDER) public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapter { public static final String JWT_TOKEN_HEADER_PARAM = "X-Authorization"; diff --git a/application/src/main/java/org/thingsboard/server/config/ThingsboardWebFluxSecurityConfig.java b/application/src/main/java/org/thingsboard/server/config/ThingsboardWebFluxSecurityConfig.java new file mode 100644 index 0000000000..bdb168f714 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/config/ThingsboardWebFluxSecurityConfig.java @@ -0,0 +1,61 @@ +/** + * Copyright © 2016-2018 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.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.ReactiveAuthenticationManager; +import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity; +import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; +import org.springframework.security.config.web.server.SecurityWebFiltersOrder; +import org.springframework.security.config.web.server.ServerHttpSecurity; +import org.springframework.security.web.server.SecurityWebFilterChain; +import org.springframework.security.web.server.context.ServerSecurityContextRepository; + +//@EnableWebFluxSecurity +//@EnableReactiveMethodSecurity +public class ThingsboardWebFluxSecurityConfig { + + private static final String WS_TOKEN_BASED_AUTH_ENTRY_POINT = "/api/ws/**"; + + @Autowired + private ReactiveAuthenticationManager webfluxAuthenticationManager; + + @Autowired + private ServerSecurityContextRepository jwtTokenSecurityContextRepository; + + @Bean + public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) { + return http + .cors() + .and() + .csrf().disable() + .formLogin().disable() + .httpBasic().disable() + .exceptionHandling() + .and() + .authenticationManager(webfluxAuthenticationManager) + .securityContextRepository(jwtTokenSecurityContextRepository) + .authorizeExchange() + .pathMatchers(WS_TOKEN_BASED_AUTH_ENTRY_POINT) + .authenticated() + .and() + .build(); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java b/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java index 59b7da2e76..4472db305e 100644 --- a/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java +++ b/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java @@ -21,38 +21,57 @@ import org.springframework.http.HttpStatus; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.web.socket.WebSocketHandler; -import org.springframework.web.socket.config.annotation.EnableWebSocket; -import org.springframework.web.socket.config.annotation.WebSocketConfigurer; -import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; -import org.springframework.web.socket.server.HandshakeInterceptor; -import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean; -import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor; +import org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository; +import org.springframework.web.reactive.HandlerMapping; +import org.springframework.web.reactive.HandlerResult; +import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping; +import org.springframework.web.reactive.socket.WebSocketHandler; +import org.springframework.web.reactive.socket.server.support.HandshakeWebSocketService; +import org.springframework.web.reactive.socket.server.support.WebSocketHandlerAdapter; +import org.springframework.web.server.ServerWebExchange; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.controller.plugin.TbWebSocketHandler; import org.thingsboard.server.service.security.model.SecurityUser; +import reactor.core.publisher.Mono; +import java.util.HashMap; import java.util.Map; @Configuration -@EnableWebSocket -public class WebSocketConfiguration implements WebSocketConfigurer { +public class WebSocketConfiguration { public static final String WS_PLUGIN_PREFIX = "/api/ws/plugins/"; public static final String WS_SECURITY_USER_ATTRIBUTE = "SECURITY_USER"; private static final String WS_PLUGIN_MAPPING = WS_PLUGIN_PREFIX + "**"; - @Bean +/* @Bean public ServletServerContainerFactoryBean createWebSocketContainer() { ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean(); container.setMaxTextMessageBufferSize(32768); container.setMaxBinaryMessageBufferSize(32768); return container; + }*/ + + @Bean + public HandlerMapping handlerMapping() { + Map map = new HashMap<>(); + map.put(WS_PLUGIN_MAPPING, wsHandler()); + + SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping(); + mapping.setUrlMap(map); + mapping.setOrder(-1); // before annotated controllers + return mapping; } - @Override + @Bean + public WebSocketHandlerAdapter handlerAdapter() { + return new WebSocketHandlerAdapter(); + } + +/* @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(wsHandler(), WS_PLUGIN_MAPPING).setAllowedOrigins("*") .addInterceptors(new HttpSessionHandshakeInterceptor(), new HandshakeInterceptor() { @@ -79,19 +98,19 @@ public class WebSocketConfiguration implements WebSocketConfigurer { //Do nothing } }); - } + }*/ @Bean public WebSocketHandler wsHandler() { return new TbWebSocketHandler(); } - protected SecurityUser getCurrentUser() throws ThingsboardException { +/* protected SecurityUser getCurrentUser() throws ThingsboardException { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication != null && authentication.getPrincipal() instanceof SecurityUser) { return (SecurityUser) authentication.getPrincipal(); } else { throw new ThingsboardException("You aren't authorized to perform this operation!", ThingsboardErrorCode.AUTHENTICATION); } - } + }*/ } diff --git a/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java b/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java index f579db8588..d01fc07e71 100644 --- a/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java +++ b/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java @@ -21,10 +21,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; -import org.springframework.web.socket.CloseStatus; -import org.springframework.web.socket.TextMessage; -import org.springframework.web.socket.WebSocketSession; -import org.springframework.web.socket.handler.TextWebSocketHandler; +import org.springframework.web.reactive.socket.CloseStatus; +import org.springframework.web.reactive.socket.WebSocketHandler; +import org.springframework.web.reactive.socket.WebSocketSession; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; @@ -37,10 +36,13 @@ import org.thingsboard.server.service.telemetry.SessionEvent; import org.thingsboard.server.service.telemetry.TelemetryWebSocketMsgEndpoint; import org.thingsboard.server.service.telemetry.TelemetryWebSocketService; import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; import java.io.IOException; import java.net.URI; import java.security.InvalidParameterException; +import java.security.Principal; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @@ -48,12 +50,12 @@ import java.util.concurrent.ConcurrentMap; @Service @Slf4j -public class TbWebSocketHandler extends TextWebSocketHandler implements TelemetryWebSocketMsgEndpoint { +public class TbWebSocketHandler implements WebSocketHandler, TelemetryWebSocketMsgEndpoint { - private static final ConcurrentMap internalSessionMap = new ConcurrentHashMap<>(); - private static final ConcurrentMap externalSessionMap = new ConcurrentHashMap<>(); + //private static final ConcurrentMap internalSessionMap = new ConcurrentHashMap<>(); + // private static final ConcurrentMap externalSessionMap = new ConcurrentHashMap<>(); - @Autowired + /* @Autowired private TelemetryWebSocketService webSocketService; @Value("${server.ws.limits.max_sessions_per_tenant:0}") @@ -90,8 +92,26 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr } catch (IOException e) { log.warn("IO error", e); } + }*/ + + @Override + public Mono handle(WebSocketSession session) { + return session.receive() + .doOnNext(message -> { + Principal principal = session.getHandshakeInfo().getPrincipal().block(); + if (principal instanceof SecurityUser) { + SecurityUser currentUser = (SecurityUser) principal; + log.info("[{}][{}] Processing {}", currentUser.getTenantId(), session.getId(), message.getPayloadAsText()); + } else { + log.info("[{}] Principal {}", session.getId(), principal); + log.info("[{}] Processing {}", session.getId(), message.getPayloadAsText()); + } + }) + .then(); } + +/* @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { super.afterConnectionEstablished(session); @@ -173,11 +193,11 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr this.session = session; this.sessionRef = sessionRef; } - } + }*/ @Override public void send(TelemetryWebSocketSessionRef sessionRef, int subscriptionId, String msg) throws IOException { - String externalId = sessionRef.getSessionId(); + /* String externalId = sessionRef.getSessionId(); log.debug("[{}] Processing {}", externalId, msg); String internalId = externalSessionMap.get(externalId); if (internalId != null) { @@ -212,12 +232,12 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr } } else { log.warn("[{}] Failed to find session by external id", externalId); - } + }*/ } @Override public void close(TelemetryWebSocketSessionRef sessionRef, CloseStatus reason) throws IOException { - String externalId = sessionRef.getSessionId(); + /* String externalId = sessionRef.getSessionId(); log.debug("[{}] Processing close request", externalId); String internalId = externalSessionMap.get(externalId); if (internalId != null) { @@ -229,10 +249,10 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr } } else { log.warn("[{}] Failed to find session by external id", externalId); - } + }*/ } - private boolean checkLimits(WebSocketSession session, TelemetryWebSocketSessionRef sessionRef) throws Exception { + /*private boolean checkLimits(WebSocketSession session, TelemetryWebSocketSessionRef sessionRef) throws Exception { String sessionId = session.getId(); if (maxSessionsPerTenant > 0) { Set tenantSessions = tenantSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getTenantId(), id -> ConcurrentHashMap.newKeySet()); @@ -322,6 +342,6 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr } } } - } + }*/ } diff --git a/application/src/main/java/org/thingsboard/server/service/install/CassandraAbstractDatabaseSchemaService.java b/application/src/main/java/org/thingsboard/server/service/install/CassandraAbstractDatabaseSchemaService.java index 10559ba2d2..f800ced5ae 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/CassandraAbstractDatabaseSchemaService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/CassandraAbstractDatabaseSchemaService.java @@ -17,6 +17,7 @@ package org.thingsboard.server.service.install; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.thingsboard.server.dao.cassandra.CassandraInstallCluster; import org.thingsboard.server.service.install.cql.CQLStatementsParser; @@ -30,6 +31,7 @@ public abstract class CassandraAbstractDatabaseSchemaService implements Database private static final String CASSANDRA_DIR = "cassandra"; @Autowired + @Qualifier("CassandraInstallCluster") private CassandraInstallCluster cluster; @Autowired diff --git a/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java index a2aeefaecf..58021a6d67 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java @@ -18,6 +18,7 @@ package org.thingsboard.server.service.install; import com.datastax.driver.core.KeyspaceMetadata; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; import org.thingsboard.server.dao.cassandra.CassandraCluster; @@ -65,6 +66,7 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { private CassandraCluster cluster; @Autowired + @Qualifier("CassandraInstallCluster") private CassandraInstallCluster installCluster; @Autowired diff --git a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java index 6c48ee144f..479c628d7e 100644 --- a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java +++ b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java @@ -18,7 +18,9 @@ package org.thingsboard.server.service.mail; import com.fasterxml.jackson.databind.JsonNode; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import org.apache.velocity.VelocityContext; import org.apache.velocity.app.VelocityEngine; +import org.apache.velocity.exception.VelocityException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.MessageSource; @@ -26,7 +28,7 @@ import org.springframework.core.NestedRuntimeException; import org.springframework.mail.javamail.JavaMailSenderImpl; import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.stereotype.Service; -import org.springframework.ui.velocity.VelocityEngineUtils; +//import org.springframework.ui.velocity.VelocityEngineUtils; import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; @@ -39,6 +41,8 @@ import org.thingsboard.server.dao.settings.AdminSettingsService; import javax.annotation.PostConstruct; import javax.mail.MessagingException; import javax.mail.internet.MimeMessage; +import java.io.StringWriter; +import java.io.Writer; import java.util.HashMap; import java.util.Locale; import java.util.Map; @@ -126,7 +130,7 @@ public class DefaultMailService implements MailService { Map model = new HashMap(); model.put(TARGET_EMAIL, email); - String message = VelocityEngineUtils.mergeTemplateIntoString(this.engine, + String message = mergeTemplateIntoString(this.engine, "test.vm", UTF_8, model); sendMail(testMailSender, mailFrom, email, subject, message); @@ -141,7 +145,7 @@ public class DefaultMailService implements MailService { model.put("activationLink", activationLink); model.put(TARGET_EMAIL, email); - String message = VelocityEngineUtils.mergeTemplateIntoString(this.engine, + String message = mergeTemplateIntoString(this.engine, "activation.vm", UTF_8, model); sendMail(mailSender, mailFrom, email, subject, message); @@ -156,7 +160,7 @@ public class DefaultMailService implements MailService { model.put("loginLink", loginLink); model.put(TARGET_EMAIL, email); - String message = VelocityEngineUtils.mergeTemplateIntoString(this.engine, + String message = mergeTemplateIntoString(this.engine, "account.activated.vm", UTF_8, model); sendMail(mailSender, mailFrom, email, subject, message); @@ -171,7 +175,7 @@ public class DefaultMailService implements MailService { model.put("passwordResetLink", passwordResetLink); model.put(TARGET_EMAIL, email); - String message = VelocityEngineUtils.mergeTemplateIntoString(this.engine, + String message = mergeTemplateIntoString(this.engine, "reset.password.vm", UTF_8, model); sendMail(mailSender, mailFrom, email, subject, message); @@ -186,7 +190,7 @@ public class DefaultMailService implements MailService { model.put("loginLink", loginLink); model.put(TARGET_EMAIL, email); - String message = VelocityEngineUtils.mergeTemplateIntoString(this.engine, + String message = mergeTemplateIntoString(this.engine, "password.was.reset.vm", UTF_8, model); sendMail(mailSender, mailFrom, email, subject, message); @@ -225,6 +229,22 @@ public class DefaultMailService implements MailService { } } + private static String mergeTemplateIntoString(VelocityEngine velocityEngine, String templateLocation, + String encoding, Map model) throws VelocityException { + + StringWriter result = new StringWriter(); + mergeTemplate(velocityEngine, templateLocation, encoding, model, result); + return result.toString(); + } + + private static void mergeTemplate( + VelocityEngine velocityEngine, String templateLocation, String encoding, + Map model, Writer writer) throws VelocityException { + + VelocityContext velocityContext = new VelocityContext(model); + velocityEngine.mergeTemplate(templateLocation, encoding, velocityContext, writer); + } + protected ThingsboardException handleException(Exception exception) { String message; if (exception instanceof NestedRuntimeException) { diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/webflux/WebfluxAuthenticationManager.java b/application/src/main/java/org/thingsboard/server/service/security/auth/webflux/WebfluxAuthenticationManager.java new file mode 100644 index 0000000000..b2aeb0ded8 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/webflux/WebfluxAuthenticationManager.java @@ -0,0 +1,48 @@ +/** + * Copyright © 2016-2018 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.service.security.auth.webflux; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.ReactiveAuthenticationManager; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Component; +import org.thingsboard.server.service.security.auth.JwtAuthenticationToken; +import org.thingsboard.server.service.security.model.SecurityUser; +import org.thingsboard.server.service.security.model.token.JwtTokenFactory; +import org.thingsboard.server.service.security.model.token.RawAccessJwtToken; +import reactor.core.publisher.Mono; + +@Component +public class WebfluxAuthenticationManager implements ReactiveAuthenticationManager { + + @Autowired + private JwtTokenFactory tokenFactory; + + @Override + public Mono authenticate(Authentication authentication) { + try { + if (authentication.getCredentials() != null && authentication.getCredentials() instanceof RawAccessJwtToken) { + RawAccessJwtToken rawAccessToken = (RawAccessJwtToken) authentication.getCredentials(); + SecurityUser securityUser = tokenFactory.parseAccessJwtToken(rawAccessToken); + JwtAuthenticationToken auth = new JwtAuthenticationToken(securityUser); + return Mono.just(auth); + } + return Mono.empty(); + } catch (Exception e) { + return Mono.error(e); + } + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/webflux/jwt/JwtTokenSecurityContextRepository.java b/application/src/main/java/org/thingsboard/server/service/security/auth/webflux/jwt/JwtTokenSecurityContextRepository.java new file mode 100644 index 0000000000..482830086e --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/webflux/jwt/JwtTokenSecurityContextRepository.java @@ -0,0 +1,86 @@ +/** + * Copyright © 2016-2018 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.service.security.auth.webflux.jwt; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.ReactiveAuthenticationManager; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextImpl; +import org.springframework.security.web.server.context.ServerSecurityContextRepository; +import org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; +import org.springframework.web.server.ServerWebExchange; +import org.thingsboard.server.config.ThingsboardSecurityConfiguration; +import org.thingsboard.server.service.security.auth.JwtAuthenticationToken; +import org.thingsboard.server.service.security.auth.jwt.extractor.TokenExtractor; +import org.thingsboard.server.service.security.model.token.RawAccessJwtToken; +import reactor.core.publisher.Mono; + +import java.util.List; + +@Component +public class JwtTokenSecurityContextRepository implements ServerSecurityContextRepository { + + public static final String DEFAULT_SPRING_SECURITY_CONTEXT_ATTR_NAME = "SPRING_SECURITY_CONTEXT"; + + @Autowired + private ReactiveAuthenticationManager webfluxAuthenticationManager; + + @Override + public Mono save(ServerWebExchange exchange, SecurityContext context) { + return exchange.getSession() + .doOnNext(session -> { + if (context == null) { + session.getAttributes().remove(WebSessionServerSecurityContextRepository.DEFAULT_SPRING_SECURITY_CONTEXT_ATTR_NAME); + } else { + session.getAttributes().put(WebSessionServerSecurityContextRepository.DEFAULT_SPRING_SECURITY_CONTEXT_ATTR_NAME, context); + } + }) + .flatMap(session -> session.changeSessionId()); + } + + @Override + public Mono load(ServerWebExchange exchange) { + ServerHttpRequest request = exchange.getRequest(); + String token = extractTokenFromQuery(request); + if (!StringUtils.isEmpty(token)) { + RawAccessJwtToken rawToken = new RawAccessJwtToken(token); + Authentication auth = new JwtAuthenticationToken(rawToken); + return this.webfluxAuthenticationManager.authenticate(auth).map((authentication) -> { + return new SecurityContextImpl(authentication); + }); + } else { + return Mono.empty(); + } + } + + private String extractTokenFromQuery(ServerHttpRequest request) { + String token = null; + if (request.getQueryParams() != null) { + List tokenParamValue = request.getQueryParams().get(ThingsboardSecurityConfiguration.JWT_TOKEN_QUERY_PARAM); + if (tokenParamValue != null && !tokenParamValue.isEmpty()) { + token = tokenParamValue.get(0); + } + } + return token; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java index 6b87e033fc..883eba8bc7 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java @@ -26,8 +26,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; -import org.springframework.web.socket.CloseStatus; -import org.springframework.web.socket.WebSocketSession; +import org.springframework.web.reactive.socket.CloseStatus; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketMsgEndpoint.java b/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketMsgEndpoint.java index b73aadff1b..0b097167ad 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketMsgEndpoint.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketMsgEndpoint.java @@ -15,7 +15,7 @@ */ package org.thingsboard.server.service.telemetry; -import org.springframework.web.socket.CloseStatus; +import org.springframework.web.reactive.socket.CloseStatus; import java.io.IOException; diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 9f60fb6a90..1512961c8c 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -282,11 +282,13 @@ spring.mvc.cors: # spring serve gzip compressed static resources spring.resources.chain: - gzipped: "true" + compressed: "true" strategy: content: enabled: "true" +spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation: "true" + # HSQLDB DAO Configuration spring: data: @@ -320,7 +322,7 @@ spring: # password: "${SPRING_DATASOURCE_PASSWORD:postgres}" # Audit log parameters -audit_log: +audit-log: # Enable/disable audit log functionality. enabled: "${AUDIT_LOG_ENABLED:true}" # Specify partitioning size for audit log by tenant id storage. Example MINUTES, HOURS, DAYS, MONTHS @@ -329,7 +331,7 @@ audit_log: default_query_period: "${AUDIT_LOG_DEFAULT_QUERY_PERIOD:30}" # Logging levels per each entity type. # Allowed values: OFF (disable), W (log write operations), RW (log read and write operations) - logging_level: + logging-level: mask: "device": "${AUDIT_LOG_MASK_DEVICE:W}" "asset": "${AUDIT_LOG_MASK_ASSET:W}" diff --git a/application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java b/application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java index 7453682644..9e3463672d 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java @@ -128,7 +128,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC String accessToken = deviceCredentials.getCredentialsId(); assertNotNull(accessToken); - String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}"; + String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1},\"timeout\": 6000}"; String deviceId = savedDevice.getId().getId().toString(); doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isRequestTimeout(), @@ -183,7 +183,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC String accessToken = deviceCredentials.getCredentialsId(); assertNotNull(accessToken); - String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}"; + String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1},\"timeout\": 6000}"; String deviceId = savedDevice.getId().getId().toString(); doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isRequestTimeout(), diff --git a/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java b/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java index c7bcfbcd33..1031215204 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java +++ b/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java @@ -18,11 +18,7 @@ package org.thingsboard.server.dao; import org.thingsboard.server.common.data.id.UUIDBased; import org.thingsboard.server.dao.model.ToData; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.UUID; +import java.util.*; public abstract class DaoUtil { @@ -50,6 +46,14 @@ public abstract class DaoUtil { return object; } + public static T getData(Optional> data) { + T object = null; + if (data.isPresent()) { + object = data.get().toData(); + } + return object; + } + public static UUID getId(UUIDBased idBased) { UUID id = null; if (idBased != null) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java index 24c6a274f1..34ae9d306a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java @@ -60,7 +60,7 @@ import static org.thingsboard.server.dao.service.Validator.validateId; @Slf4j @Service -@ConditionalOnProperty(prefix = "audit_log", value = "enabled", havingValue = "true") +@ConditionalOnProperty(prefix = "audit-log", value = "enabled", havingValue = "true") public class AuditLogServiceImpl implements AuditLogService { private static final ObjectMapper objectMapper = new ObjectMapper(); diff --git a/dao/src/main/java/org/thingsboard/server/dao/audit/CassandraAuditLogDao.java b/dao/src/main/java/org/thingsboard/server/dao/audit/CassandraAuditLogDao.java index f2b2973996..ccc1880a12 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/audit/CassandraAuditLogDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/audit/CassandraAuditLogDao.java @@ -88,11 +88,11 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao redisTemplate(RedisConnectionFactory cf) { - RedisTemplate redisTemplate = new RedisTemplate<>(); - redisTemplate.setConnectionFactory(cf); - return redisTemplate; - } - - @Bean - public CacheManager cacheManager(RedisTemplate redisTemplate) { - return new RedisCacheManager(redisTemplate); + public CacheManager cacheManager(RedisConnectionFactory cf) { + DefaultFormattingConversionService redisConversionService = new DefaultFormattingConversionService(); + RedisCacheConfiguration.registerDefaultConverters(redisConversionService); + registerDefaultConverters(redisConversionService); + RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig().withConversionService(redisConversionService); + return RedisCacheManager.builder(cf).cacheDefaults(configuration).build(); } @Bean @@ -73,5 +93,8 @@ public class TBRedisCacheConfiguration { return new PreviousDeviceCredentialsIdKeyGenerator(); } - + private static void registerDefaultConverters(ConverterRegistry registry) { + Assert.notNull(registry, "ConverterRegistry must not be null!"); + registry.addConverter(EntityId.class, String.class, EntityId::toString); + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/cassandra/CassandraCluster.java b/dao/src/main/java/org/thingsboard/server/dao/cassandra/CassandraCluster.java index 19409ad146..c0c4853387 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/cassandra/CassandraCluster.java +++ b/dao/src/main/java/org/thingsboard/server/dao/cassandra/CassandraCluster.java @@ -21,7 +21,7 @@ import org.thingsboard.server.dao.util.NoSqlAnyDao; import javax.annotation.PostConstruct; -@Component +@Component("CassandraCluster") @NoSqlAnyDao public class CassandraCluster extends AbstractCassandraCluster { diff --git a/dao/src/main/java/org/thingsboard/server/dao/cassandra/CassandraInstallCluster.java b/dao/src/main/java/org/thingsboard/server/dao/cassandra/CassandraInstallCluster.java index 247a204ee5..718a4fa299 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/cassandra/CassandraInstallCluster.java +++ b/dao/src/main/java/org/thingsboard/server/dao/cassandra/CassandraInstallCluster.java @@ -21,7 +21,7 @@ import org.thingsboard.server.dao.util.NoSqlAnyDao; import javax.annotation.PostConstruct; -@Component +@Component("CassandraInstallCluster") @NoSqlAnyDao @Profile("install") public class CassandraInstallCluster extends AbstractCassandraCluster { diff --git a/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraAbstractDao.java b/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraAbstractDao.java index 1a419df9a1..ebcc4f61dd 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraAbstractDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraAbstractDao.java @@ -27,6 +27,7 @@ import com.datastax.driver.core.TypeCodec; import com.datastax.driver.core.exceptions.CodecNotFoundException; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.cassandra.CassandraCluster; import org.thingsboard.server.dao.model.type.AuthorityCodec; @@ -44,6 +45,7 @@ import java.util.concurrent.ConcurrentMap; public abstract class CassandraAbstractDao { @Autowired + @Qualifier("CassandraCluster") protected CassandraCluster cluster; private ConcurrentMap preparedStatementMap = new ConcurrentHashMap<>(); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java index 987eabe738..4c8eb372e5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java @@ -27,6 +27,7 @@ import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.model.BaseEntity; import java.util.List; +import java.util.Optional; import java.util.UUID; import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUID; @@ -67,23 +68,23 @@ public abstract class JpaAbstractDao, D> @Override public D findById(TenantId tenantId, UUID key) { log.debug("Get entity by key {}", key); - E entity = getCrudRepository().findOne(fromTimeUUID(key)); + Optional entity = getCrudRepository().findById(fromTimeUUID(key)); return DaoUtil.getData(entity); } @Override public ListenableFuture findByIdAsync(TenantId tenantId, UUID key) { log.debug("Get entity by key async {}", key); - return service.submit(() -> DaoUtil.getData(getCrudRepository().findOne(fromTimeUUID(key)))); + return service.submit(() -> DaoUtil.getData(getCrudRepository().findById(fromTimeUUID(key)))); } @Override @Transactional public boolean removeById(TenantId tenantId, UUID id) { String key = fromTimeUUID(id); - getCrudRepository().delete(key); + getCrudRepository().deleteById(key); log.debug("Remove request: {}", key); - return getCrudRepository().findOne(key) == null; + return !getCrudRepository().existsById(key); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java index 2193646ec9..84ef8b88a8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java @@ -52,7 +52,7 @@ public class JpaAttributeDao extends JpaAbstractDaoListeningExecutorService impl AttributeKvCompositeKey compositeKey = getAttributeKvCompositeKey(entityId, attributeType, attributeKey); return Futures.immediateFuture( - Optional.ofNullable(DaoUtil.getData(attributeKvRepository.findOne(compositeKey)))); + Optional.ofNullable(DaoUtil.getData(attributeKvRepository.findById(compositeKey)))); } @Override @@ -64,7 +64,7 @@ public class JpaAttributeDao extends JpaAbstractDaoListeningExecutorService impl getAttributeKvCompositeKey(entityId, attributeType, attributeKey)) .collect(Collectors.toList()); return Futures.immediateFuture( - DaoUtil.convertDataList(Lists.newArrayList(attributeKvRepository.findAll(compositeKeys)))); + DaoUtil.convertDataList(Lists.newArrayList(attributeKvRepository.findAllById(compositeKeys)))); } @Override @@ -103,7 +103,7 @@ public class JpaAttributeDao extends JpaAbstractDaoListeningExecutorService impl }).collect(Collectors.toList()); return service.submit(() -> { - attributeKvRepository.delete(entitiesToDelete); + attributeKvRepository.deleteAll(entitiesToDelete); return null; }); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/component/JpaBaseComponentDescriptorDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/component/JpaBaseComponentDescriptorDao.java index 47b006e3b1..56b0ec7e7f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/component/JpaBaseComponentDescriptorDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/component/JpaBaseComponentDescriptorDao.java @@ -66,7 +66,7 @@ public class JpaBaseComponentDescriptorDao extends JpaAbstractSearchTextDao checkRelation(TenantId tenantId, EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { RelationCompositeKey key = getRelationCompositeKey(from, to, relationType, typeGroup); - return service.submit(() -> relationRepository.findOne(key) != null); + return service.submit(() -> relationRepository.existsById(key)); } @Override public ListenableFuture getRelation(TenantId tenantId, EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { RelationCompositeKey key = getRelationCompositeKey(from, to, relationType, typeGroup); - return service.submit(() -> DaoUtil.getData(relationRepository.findOne(key))); + return service.submit(() -> DaoUtil.getData(relationRepository.findById(key))); } private RelationCompositeKey getRelationCompositeKey(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { @@ -152,9 +152,9 @@ public class JpaRelationDao extends JpaAbstractDaoListeningExecutorService imple } private boolean deleteRelationIfExists(RelationCompositeKey key) { - boolean relationExistsBeforeDelete = relationRepository.exists(key); + boolean relationExistsBeforeDelete = relationRepository.existsById(key); if (relationExistsBeforeDelete) { - relationRepository.delete(key); + relationRepository.deleteById(key); } return relationExistsBeforeDelete; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/relation/RelationRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/relation/RelationRepository.java index 60c6a0e9d7..cf1e2b22b0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/relation/RelationRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/relation/RelationRepository.java @@ -53,7 +53,7 @@ public interface RelationRepository RelationEntity save(RelationEntity entity); @Transactional - void delete(RelationCompositeKey id); + void deleteById(RelationCompositeKey id); @Transactional void deleteByFromIdAndFromType(String fromId, String fromType); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/JpaTimeseriesDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/JpaTimeseriesDao.java index 312014861f..7fc28b0ec3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/JpaTimeseriesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/JpaTimeseriesDao.java @@ -258,10 +258,10 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp entityId.getEntityType(), fromTimeUUID(entityId.getId()), key); - TsKvLatestEntity entry = tsKvLatestRepository.findOne(compositeKey); + Optional entry = tsKvLatestRepository.findById(compositeKey); TsKvEntry result; - if (entry != null) { - result = DaoUtil.getData(entry); + if (entry.isPresent()) { + result = DaoUtil.getData(entry.get()); } else { result = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(key, null)); } diff --git a/dao/src/test/resources/application-test.properties b/dao/src/test/resources/application-test.properties index a285676f08..c873c908db 100644 --- a/dao/src/test/resources/application-test.properties +++ b/dao/src/test/resources/application-test.properties @@ -4,10 +4,10 @@ zk.zk_dir=/thingsboard updates.enabled=false -audit_log.enabled=true -audit_log.by_tenant_partitioning=MONTHS -audit_log.default_query_period=30 -audit_log.sink.type=none +audit-log.enabled=true +audit-log.by_tenant_partitioning=MONTHS +audit-log.default_query_period=30 +audit-log.sink.type=none cache.type=caffeine #cache.type=redis diff --git a/dao/src/test/resources/sql-test.properties b/dao/src/test/resources/sql-test.properties index 3357425fce..745aa9e1e0 100644 --- a/dao/src/test/resources/sql-test.properties +++ b/dao/src/test/resources/sql-test.properties @@ -4,6 +4,7 @@ database.entities.type=sql sql.ts_inserts_executor_type=fixed sql.ts_inserts_fixed_thread_pool_size=10 +spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true spring.jpa.show-sql=false spring.jpa.hibernate.ddl-auto=validate spring.jpa.database-platform=org.hibernate.dialect.HSQLDialect diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 944ed66880..b89a214b39 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -23,6 +23,9 @@ services: image: "zookeeper:3.5" ports: - "2181" + environment: + ZOO_MY_ID: 1 + ZOO_SERVERS: server.1=zookeeper:2888:3888;zookeeper:2181 kafka: restart: always image: "wurstmeister/kafka" diff --git a/pom.xml b/pom.xml index 00c9c28567..27cb40121b 100755 --- a/pom.xml +++ b/pom.xml @@ -29,10 +29,10 @@ ${basedir} - 1.4.3.RELEASE - 4.3.4.RELEASE - 4.2.0.RELEASE - 1.8.10.RELEASE + 2.1.0.RELEASE + 5.1.2.RELEASE + 5.1.1.RELEASE + 2.1.2.RELEASE 2.9.0 0.7.0 2.2.0 @@ -41,8 +41,8 @@ 1.2.3 1.9.5 0.10 - 3.5.0 - 3.3.0.2 + 3.6.0 + 3.5.0.1 1.2.7 21.0 2.6.1 @@ -50,7 +50,7 @@ 1.5.0 2.5 1.4 - 2.8.11.1 + 2.9.7 2.2.6 2.11 2.4.2 @@ -58,13 +58,15 @@ 2.6.2 1.7 2.0 + 2.7.7 + 1.23 1.4.3 4.0.1 - 3.0.2 - 1.12.0 + 3.6.1 + 1.16.1 1.16.18 1.1.0 - 4.1.22.Final + 4.1.30.Final 1.5.0 4.8.0 2.19.1 @@ -410,7 +412,7 @@ org.springframework.boot - spring-boot-starter-websocket + spring-boot-starter-webflux ${spring-boot.version} @@ -485,20 +487,16 @@ org.apache.velocity velocity-tools ${velocity-tools.version} - - - javax.servlet - servlet-api - - - dom4j - dom4j - - - antlr - antlr - - + + + antlr + antlr + ${antlr.version} + + + org.yaml + snakeyaml + ${snakeyaml.version} com.rabbitmq From 1022a3dc0183eada6f9c139f716e4064ddc6ebdf Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Fri, 22 Feb 2019 14:15:49 +0200 Subject: [PATCH 02/19] Exlude old version of jackson-core that doesn't work with updated Spring framework --- pom.xml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f12049aaac..eb0650a124 100755 --- a/pom.xml +++ b/pom.xml @@ -50,7 +50,7 @@ 1.5.0 2.5 1.4 - 2.9.7 + 2.9.0 2.2.6 2.11 2.4.2 @@ -792,6 +792,12 @@ de.ruedigermoeller fst ${fst.version} + + + com.fasterxml.jackson.core + jackson-core + + io.springfox.ui From 83d4dbc6e5426f30c1a74655460e052c11204350 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Fri, 22 Feb 2019 15:15:27 +0200 Subject: [PATCH 03/19] Websockets reverted --- application/pom.xml | 2 +- .../ThingsboardWebFluxSecurityConfig.java | 60 ------------- .../server/config/WebSocketConfiguration.java | 51 ++++------- .../controller/plugin/TbWebSocketHandler.java | 85 +++++++----------- .../webflux/WebfluxAuthenticationManager.java | 48 ----------- .../JwtTokenSecurityContextRepository.java | 86 ------------------- .../DefaultTelemetryWebSocketService.java | 2 +- .../TelemetryWebSocketMsgEndpoint.java | 2 +- pom.xml | 4 +- 9 files changed, 54 insertions(+), 286 deletions(-) delete mode 100644 application/src/main/java/org/thingsboard/server/config/ThingsboardWebFluxSecurityConfig.java delete mode 100644 application/src/main/java/org/thingsboard/server/service/security/auth/webflux/WebfluxAuthenticationManager.java delete mode 100644 application/src/main/java/org/thingsboard/server/service/security/auth/webflux/jwt/JwtTokenSecurityContextRepository.java diff --git a/application/pom.xml b/application/pom.xml index 91fe9f71f2..91965dfed5 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -126,7 +126,7 @@ org.springframework.boot - spring-boot-starter-webflux + spring-boot-starter-websocket io.jsonwebtoken diff --git a/application/src/main/java/org/thingsboard/server/config/ThingsboardWebFluxSecurityConfig.java b/application/src/main/java/org/thingsboard/server/config/ThingsboardWebFluxSecurityConfig.java deleted file mode 100644 index db8f599350..0000000000 --- a/application/src/main/java/org/thingsboard/server/config/ThingsboardWebFluxSecurityConfig.java +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Copyright © 2016-2019 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.config; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.ReactiveAuthenticationManager; -import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity; -import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; -import org.springframework.security.config.web.server.SecurityWebFiltersOrder; -import org.springframework.security.config.web.server.ServerHttpSecurity; -import org.springframework.security.web.server.SecurityWebFilterChain; -import org.springframework.security.web.server.context.ServerSecurityContextRepository; - -//@EnableWebFluxSecurity -//@EnableReactiveMethodSecurity -public class ThingsboardWebFluxSecurityConfig { - - private static final String WS_TOKEN_BASED_AUTH_ENTRY_POINT = "/api/ws/**"; - - @Autowired - private ReactiveAuthenticationManager webfluxAuthenticationManager; - - @Autowired - private ServerSecurityContextRepository jwtTokenSecurityContextRepository; - - @Bean - public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) { - return http - .cors() - .and() - .csrf().disable() - .formLogin().disable() - .httpBasic().disable() - .exceptionHandling() - .and() - .authenticationManager(webfluxAuthenticationManager) - .securityContextRepository(jwtTokenSecurityContextRepository) - .authorizeExchange() - .pathMatchers(WS_TOKEN_BASED_AUTH_ENTRY_POINT) - .authenticated() - .and() - .build(); - } - -} diff --git a/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java b/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java index 6c08b1b9a6..b8b703e4fc 100644 --- a/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java +++ b/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java @@ -21,63 +21,44 @@ import org.springframework.http.HttpStatus; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository; -import org.springframework.web.reactive.HandlerMapping; -import org.springframework.web.reactive.HandlerResult; -import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping; -import org.springframework.web.reactive.socket.WebSocketHandler; -import org.springframework.web.reactive.socket.server.support.HandshakeWebSocketService; -import org.springframework.web.reactive.socket.server.support.WebSocketHandlerAdapter; -import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.socket.WebSocketHandler; +import org.springframework.web.socket.config.annotation.EnableWebSocket; +import org.springframework.web.socket.config.annotation.WebSocketConfigurer; +import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; +import org.springframework.web.socket.server.HandshakeInterceptor; +import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean; +import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.controller.plugin.TbWebSocketHandler; import org.thingsboard.server.service.security.model.SecurityUser; -import reactor.core.publisher.Mono; -import java.util.HashMap; import java.util.Map; @Configuration -public class WebSocketConfiguration { +@EnableWebSocket +public class WebSocketConfiguration implements WebSocketConfigurer { public static final String WS_PLUGIN_PREFIX = "/api/ws/plugins/"; private static final String WS_PLUGIN_MAPPING = WS_PLUGIN_PREFIX + "**"; -/* @Bean + @Bean public ServletServerContainerFactoryBean createWebSocketContainer() { ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean(); container.setMaxTextMessageBufferSize(32768); container.setMaxBinaryMessageBufferSize(32768); return container; - }*/ - - @Bean - public HandlerMapping handlerMapping() { - Map map = new HashMap<>(); - map.put(WS_PLUGIN_MAPPING, wsHandler()); - - SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping(); - mapping.setUrlMap(map); - mapping.setOrder(-1); // before annotated controllers - return mapping; } - @Bean - public WebSocketHandlerAdapter handlerAdapter() { - return new WebSocketHandlerAdapter(); - } - -/* @Override + @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(wsHandler(), WS_PLUGIN_MAPPING).setAllowedOrigins("*") .addInterceptors(new HttpSessionHandshakeInterceptor(), new HandshakeInterceptor() { @Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, - Map attributes) throws Exception { + Map attributes) throws Exception { SecurityUser user = null; try { user = getCurrentUser(); @@ -92,23 +73,23 @@ public class WebSocketConfiguration { @Override public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, - Exception exception) { + Exception exception) { //Do nothing } }); - }*/ + } @Bean public WebSocketHandler wsHandler() { return new TbWebSocketHandler(); } -/* protected SecurityUser getCurrentUser() throws ThingsboardException { + protected SecurityUser getCurrentUser() throws ThingsboardException { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication != null && authentication.getPrincipal() instanceof SecurityUser) { return (SecurityUser) authentication.getPrincipal(); } else { throw new ThingsboardException("You aren't authorized to perform this operation!", ThingsboardErrorCode.AUTHENTICATION); } - }*/ + } } diff --git a/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java b/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java index d20e4db433..169bba8895 100644 --- a/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java +++ b/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java @@ -16,51 +16,48 @@ package org.thingsboard.server.controller.plugin; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.BeanCreationNotAllowedException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.Authentication; import org.springframework.stereotype.Service; -import org.springframework.web.reactive.socket.CloseStatus; -import org.springframework.web.reactive.socket.WebSocketHandler; -import org.springframework.web.reactive.socket.WebSocketSession; +import org.springframework.util.StringUtils; +import org.springframework.web.socket.CloseStatus; +import org.springframework.web.socket.TextMessage; +import org.springframework.web.socket.WebSocketSession; +import org.springframework.web.socket.adapter.NativeWebSocketSession; +import org.springframework.web.socket.handler.TextWebSocketHandler; +import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.msg.tools.TbRateLimits; +import org.thingsboard.server.config.WebSocketConfiguration; import org.thingsboard.server.service.security.model.SecurityUser; +import org.thingsboard.server.service.security.model.UserPrincipal; +import org.thingsboard.server.service.telemetry.SessionEvent; import org.thingsboard.server.service.telemetry.TelemetryWebSocketMsgEndpoint; +import org.thingsboard.server.service.telemetry.TelemetryWebSocketService; import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef; -import reactor.core.publisher.Mono; +import javax.websocket.*; import java.io.IOException; -import java.security.Principal; +import java.net.URI; +import java.security.InvalidParameterException; +import java.util.Queue; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.LinkedBlockingQueue; @Service @Slf4j -public class TbWebSocketHandler implements WebSocketHandler, TelemetryWebSocketMsgEndpoint { +public class TbWebSocketHandler extends TextWebSocketHandler implements TelemetryWebSocketMsgEndpoint { - @Override - public Mono handle(WebSocketSession session) { - return session.receive() - .doOnNext(message -> { - Principal principal = session.getHandshakeInfo().getPrincipal().block(); - if (principal instanceof SecurityUser) { - SecurityUser currentUser = (SecurityUser) principal; - log.info("[{}][{}] Processing {}", currentUser.getTenantId(), session.getId(), message.getPayloadAsText()); - } else { - log.info("[{}] Principal {}", session.getId(), principal); - log.info("[{}] Processing {}", session.getId(), message.getPayloadAsText()); - } - }) - .then(); - } + private static final ConcurrentMap internalSessionMap = new ConcurrentHashMap<>(); + private static final ConcurrentMap externalSessionMap = new ConcurrentHashMap<>(); - @Override - public void send(TelemetryWebSocketSessionRef sessionRef, int subscriptionId, String msg) throws IOException { - - } - - @Override - public void close(TelemetryWebSocketSessionRef sessionRef, CloseStatus withReason) throws IOException { - - } - -// private static final ConcurrentMap internalSessionMap = new ConcurrentHashMap<>(); -// private static final ConcurrentMap externalSessionMap = new ConcurrentHashMap<>(); -/* @Autowired private TelemetryWebSocketService webSocketService; @@ -105,22 +102,6 @@ public class TbWebSocketHandler implements WebSocketHandler, TelemetryWebSocketM } } - @Override - public Mono handle(WebSocketSession session) { - return session.receive() - .doOnNext(message -> { - Principal principal = session.getHandshakeInfo().getPrincipal().block(); - if (principal instanceof SecurityUser) { - SecurityUser currentUser = (SecurityUser) principal; - log.info("[{}][{}] Processing {}", currentUser.getTenantId(), session.getId(), message.getPayloadAsText()); - } else { - log.info("[{}] Principal {}", session.getId(), principal); - log.info("[{}] Processing {}", session.getId(), message.getPayloadAsText()); - } - }) - .then(); - } - @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { super.afterConnectionEstablished(session); @@ -410,5 +391,5 @@ public class TbWebSocketHandler implements WebSocketHandler, TelemetryWebSocketM } } } - */ -} + +} \ No newline at end of file diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/webflux/WebfluxAuthenticationManager.java b/application/src/main/java/org/thingsboard/server/service/security/auth/webflux/WebfluxAuthenticationManager.java deleted file mode 100644 index 35bee6e9bd..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/webflux/WebfluxAuthenticationManager.java +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright © 2016-2019 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.service.security.auth.webflux; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.authentication.ReactiveAuthenticationManager; -import org.springframework.security.core.Authentication; -import org.springframework.stereotype.Component; -import org.thingsboard.server.service.security.auth.JwtAuthenticationToken; -import org.thingsboard.server.service.security.model.SecurityUser; -import org.thingsboard.server.service.security.model.token.JwtTokenFactory; -import org.thingsboard.server.service.security.model.token.RawAccessJwtToken; -import reactor.core.publisher.Mono; - -@Component -public class WebfluxAuthenticationManager implements ReactiveAuthenticationManager { - - @Autowired - private JwtTokenFactory tokenFactory; - - @Override - public Mono authenticate(Authentication authentication) { - try { - if (authentication.getCredentials() != null && authentication.getCredentials() instanceof RawAccessJwtToken) { - RawAccessJwtToken rawAccessToken = (RawAccessJwtToken) authentication.getCredentials(); - SecurityUser securityUser = tokenFactory.parseAccessJwtToken(rawAccessToken); - JwtAuthenticationToken auth = new JwtAuthenticationToken(securityUser); - return Mono.just(auth); - } - return Mono.empty(); - } catch (Exception e) { - return Mono.error(e); - } - } -} diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/webflux/jwt/JwtTokenSecurityContextRepository.java b/application/src/main/java/org/thingsboard/server/service/security/auth/webflux/jwt/JwtTokenSecurityContextRepository.java deleted file mode 100644 index f6fe103fc5..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/webflux/jwt/JwtTokenSecurityContextRepository.java +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Copyright © 2016-2019 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.service.security.auth.webflux.jwt; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.ReactiveAuthenticationManager; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.context.SecurityContextImpl; -import org.springframework.security.web.server.context.ServerSecurityContextRepository; -import org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository; -import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; -import org.springframework.web.server.ServerWebExchange; -import org.thingsboard.server.config.ThingsboardSecurityConfiguration; -import org.thingsboard.server.service.security.auth.JwtAuthenticationToken; -import org.thingsboard.server.service.security.auth.jwt.extractor.TokenExtractor; -import org.thingsboard.server.service.security.model.token.RawAccessJwtToken; -import reactor.core.publisher.Mono; - -import java.util.List; - -@Component -public class JwtTokenSecurityContextRepository implements ServerSecurityContextRepository { - - public static final String DEFAULT_SPRING_SECURITY_CONTEXT_ATTR_NAME = "SPRING_SECURITY_CONTEXT"; - - @Autowired - private ReactiveAuthenticationManager webfluxAuthenticationManager; - - @Override - public Mono save(ServerWebExchange exchange, SecurityContext context) { - return exchange.getSession() - .doOnNext(session -> { - if (context == null) { - session.getAttributes().remove(WebSessionServerSecurityContextRepository.DEFAULT_SPRING_SECURITY_CONTEXT_ATTR_NAME); - } else { - session.getAttributes().put(WebSessionServerSecurityContextRepository.DEFAULT_SPRING_SECURITY_CONTEXT_ATTR_NAME, context); - } - }) - .flatMap(session -> session.changeSessionId()); - } - - @Override - public Mono load(ServerWebExchange exchange) { - ServerHttpRequest request = exchange.getRequest(); - String token = extractTokenFromQuery(request); - if (!StringUtils.isEmpty(token)) { - RawAccessJwtToken rawToken = new RawAccessJwtToken(token); - Authentication auth = new JwtAuthenticationToken(rawToken); - return this.webfluxAuthenticationManager.authenticate(auth).map((authentication) -> { - return new SecurityContextImpl(authentication); - }); - } else { - return Mono.empty(); - } - } - - private String extractTokenFromQuery(ServerHttpRequest request) { - String token = null; - if (request.getQueryParams() != null) { - List tokenParamValue = request.getQueryParams().get(ThingsboardSecurityConfiguration.JWT_TOKEN_QUERY_PARAM); - if (tokenParamValue != null && !tokenParamValue.isEmpty()) { - token = tokenParamValue.get(0); - } - } - return token; - } - -} diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java index 68646153a6..86747150c9 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java @@ -26,7 +26,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; -import org.springframework.web.reactive.socket.CloseStatus; +import org.springframework.web.socket.CloseStatus; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketMsgEndpoint.java b/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketMsgEndpoint.java index ca3a91600f..8c18f2d42b 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketMsgEndpoint.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketMsgEndpoint.java @@ -15,7 +15,7 @@ */ package org.thingsboard.server.service.telemetry; -import org.springframework.web.reactive.socket.CloseStatus; +import org.springframework.web.socket.CloseStatus; import java.io.IOException; diff --git a/pom.xml b/pom.xml index eb0650a124..d3ff7a1635 100755 --- a/pom.xml +++ b/pom.xml @@ -50,7 +50,7 @@ 1.5.0 2.5 1.4 - 2.9.0 + 2.9.7 2.2.6 2.11 2.4.2 @@ -424,7 +424,7 @@ org.springframework.boot - spring-boot-starter-webflux + spring-boot-starter-websocket ${spring-boot.version} From e543608275a39213f405318399cff28701cfeea1 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Fri, 22 Feb 2019 16:14:36 +0200 Subject: [PATCH 04/19] Removed unused import --- .../org/thingsboard/server/service/mail/DefaultMailService.java | 1 - 1 file changed, 1 deletion(-) diff --git a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java index b9398c2a43..217d62b54e 100644 --- a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java +++ b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java @@ -28,7 +28,6 @@ import org.springframework.core.NestedRuntimeException; import org.springframework.mail.javamail.JavaMailSenderImpl; import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.stereotype.Service; -//import org.springframework.ui.velocity.VelocityEngineUtils; import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; From f009c436781994fa4a7edf3a015d41f122cb59f5 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Fri, 22 Feb 2019 16:52:04 +0200 Subject: [PATCH 05/19] Attempt to fix test --- application/src/main/resources/thingsboard.yml | 2 -- dao/src/test/resources/sql-test.properties | 1 - 2 files changed, 3 deletions(-) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 72b8159407..57d3ebbcf9 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -298,8 +298,6 @@ spring.resources.chain: content: enabled: "true" -spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation: "true" - # HSQLDB DAO Configuration spring: data: diff --git a/dao/src/test/resources/sql-test.properties b/dao/src/test/resources/sql-test.properties index 745aa9e1e0..3357425fce 100644 --- a/dao/src/test/resources/sql-test.properties +++ b/dao/src/test/resources/sql-test.properties @@ -4,7 +4,6 @@ database.entities.type=sql sql.ts_inserts_executor_type=fixed sql.ts_inserts_fixed_thread_pool_size=10 -spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true spring.jpa.show-sql=false spring.jpa.hibernate.ddl-auto=validate spring.jpa.database-platform=org.hibernate.dialect.HSQLDialect From 54c9dc1f97b62f1a041db91d052fc11d72f83675 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Fri, 22 Feb 2019 18:18:57 +0200 Subject: [PATCH 06/19] Attempt for test fix --- application/src/main/resources/thingsboard.yml | 2 ++ dao/src/test/resources/sql-test.properties | 1 + pom.xml | 6 ++++++ 3 files changed, 9 insertions(+) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 57d3ebbcf9..72b8159407 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -298,6 +298,8 @@ spring.resources.chain: content: enabled: "true" +spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation: "true" + # HSQLDB DAO Configuration spring: data: diff --git a/dao/src/test/resources/sql-test.properties b/dao/src/test/resources/sql-test.properties index 3357425fce..745aa9e1e0 100644 --- a/dao/src/test/resources/sql-test.properties +++ b/dao/src/test/resources/sql-test.properties @@ -4,6 +4,7 @@ database.entities.type=sql sql.ts_inserts_executor_type=fixed sql.ts_inserts_fixed_thread_pool_size=10 +spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true spring.jpa.show-sql=false spring.jpa.hibernate.ddl-auto=validate spring.jpa.database-platform=org.hibernate.dialect.HSQLDialect diff --git a/pom.xml b/pom.xml index d3ff7a1635..113a8f223b 100755 --- a/pom.xml +++ b/pom.xml @@ -442,6 +442,12 @@ org.springframework.boot spring-boot-starter-data-jpa ${spring-boot.version} + + + net.bytebuddy + byte-buddy + + org.apache.kafka From 76ba4d92c40a9be82b6a540c69561e590d08a913 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Fri, 22 Feb 2019 18:26:01 +0200 Subject: [PATCH 07/19] Attempt for test fix --- pom.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 113a8f223b..750da6b013 100755 --- a/pom.xml +++ b/pom.xml @@ -437,11 +437,6 @@ spring-boot-starter-test ${spring-boot.version} test - - - org.springframework.boot - spring-boot-starter-data-jpa - ${spring-boot.version} net.bytebuddy @@ -449,6 +444,11 @@ + + org.springframework.boot + spring-boot-starter-data-jpa + ${spring-boot.version} + org.apache.kafka kafka-clients From 1f89ba39e67b9056c21a5ca1d44a040685221971 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Fri, 22 Feb 2019 19:06:06 +0200 Subject: [PATCH 08/19] Attempt for test fix --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 750da6b013..44c35d9261 100755 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.6.2 1.7 2.0 - 2.7.7 + 3.5.2 1.23 1.4.3 4.0.1 From a76ff7007b3cdb395f3d9501f1aa49e04f0a569e Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Fri, 22 Feb 2019 19:10:24 +0200 Subject: [PATCH 09/19] Attempt for test fix --- pom.xml | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/pom.xml b/pom.xml index 44c35d9261..e01e7918d2 100755 --- a/pom.xml +++ b/pom.xml @@ -58,8 +58,6 @@ 2.6.2 1.7 2.0 - 3.5.2 - 1.23 1.4.3 4.0.1 3.6.1 @@ -505,16 +503,20 @@ org.apache.velocity velocity-tools ${velocity-tools.version} - - - antlr - antlr - ${antlr.version} - - - org.yaml - snakeyaml - ${snakeyaml.version} + + + javax.servlet + servlet-api + + + dom4j + dom4j + + + antlr + antlr + + com.rabbitmq From 86bffde8502932a4ee773623ae109dfafa830492 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Mon, 25 Feb 2019 10:25:15 +0200 Subject: [PATCH 10/19] Revert antlr and snakeyaml dependencies --- pom.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pom.xml b/pom.xml index e01e7918d2..5e6e59df4f 100755 --- a/pom.xml +++ b/pom.xml @@ -85,6 +85,8 @@ 2.0.0 4.1.1 2.57 + 2.7.7 + 1.23 @@ -518,6 +520,16 @@ + + org.yaml + snakeyaml + ${snakeyaml.version} + + + antlr + antlr + ${antlr.version} + com.rabbitmq amqp-client From 8545938c43c7e0c19539989aabd86daea99b60b3 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Mon, 25 Feb 2019 12:16:55 +0200 Subject: [PATCH 11/19] Exclude assertj-core to remove conflict dependency of bytebuddy --- application/paho14876741547223-tcplocalhost1883/.lck | 0 pom.xml | 4 ++++ 2 files changed, 4 insertions(+) create mode 100644 application/paho14876741547223-tcplocalhost1883/.lck diff --git a/application/paho14876741547223-tcplocalhost1883/.lck b/application/paho14876741547223-tcplocalhost1883/.lck new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pom.xml b/pom.xml index 5e6e59df4f..de61a9c317 100755 --- a/pom.xml +++ b/pom.xml @@ -442,6 +442,10 @@ net.bytebuddy byte-buddy + + org.assertj + assertj-core + From e86de7f30a9d20f15e5a0da6176aef87eb6f6026 Mon Sep 17 00:00:00 2001 From: VoBa Date: Mon, 25 Feb 2019 12:18:43 +0200 Subject: [PATCH 12/19] Delete .lck --- application/paho14876741547223-tcplocalhost1883/.lck | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 application/paho14876741547223-tcplocalhost1883/.lck diff --git a/application/paho14876741547223-tcplocalhost1883/.lck b/application/paho14876741547223-tcplocalhost1883/.lck deleted file mode 100644 index e69de29bb2..0000000000 From 42f222dbac6009ee7d00d06a661ecb79850d5781 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 6 Mar 2019 13:14:46 +0200 Subject: [PATCH 13/19] Fix main pom --- pom.xml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/pom.xml b/pom.xml index de61a9c317..dcaad0a9e4 100755 --- a/pom.xml +++ b/pom.xml @@ -437,16 +437,6 @@ spring-boot-starter-test ${spring-boot.version} test - - - net.bytebuddy - byte-buddy - - - org.assertj - assertj-core - - org.springframework.boot From 378b893db9d233c929898bcb04bb0efc997ef11e Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 6 Mar 2019 13:58:37 +0200 Subject: [PATCH 14/19] Update dependency versions --- pom.xml | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index dcaad0a9e4..8a6a9a7351 100755 --- a/pom.xml +++ b/pom.xml @@ -29,10 +29,10 @@ ${basedir} - 2.1.0.RELEASE - 5.1.2.RELEASE - 5.1.1.RELEASE - 2.1.2.RELEASE + 2.1.3.RELEASE + 5.1.5.RELEASE + 5.1.4.RELEASE + 2.1.5.RELEASE 2.9.0 0.7.0 2.2.0 @@ -87,6 +87,7 @@ 2.57 2.7.7 1.23 + 1.9.3 @@ -524,6 +525,16 @@ antlr ${antlr.version} + + net.bytebuddy + byte-buddy + ${bytebuddy.version} + + + net.bytebuddy + byte-buddy-agent + ${bytebuddy.version} + com.rabbitmq amqp-client From 4c7468102198fe06a406fb74e07a258b51d58bd6 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 6 Mar 2019 15:57:43 +0200 Subject: [PATCH 15/19] Diagnostic output --- .../thingsboard/server/rules/RuleEngineSqlTestSuite.java | 8 ++++++++ .../org/thingsboard/server/system/SystemSqlTestSuite.java | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/application/src/test/java/org/thingsboard/server/rules/RuleEngineSqlTestSuite.java b/application/src/test/java/org/thingsboard/server/rules/RuleEngineSqlTestSuite.java index c49a029dc4..d4b04289a8 100644 --- a/application/src/test/java/org/thingsboard/server/rules/RuleEngineSqlTestSuite.java +++ b/application/src/test/java/org/thingsboard/server/rules/RuleEngineSqlTestSuite.java @@ -28,6 +28,14 @@ import java.util.Arrays; "org.thingsboard.server.rules.lifecycle.sql.*Test"}) public class RuleEngineSqlTestSuite { + static { + SecurityManager appsm = System.getSecurityManager(); + System.out.println("SECURITY MANAGER = " + appsm); + if (appsm != null) { + System.out.println("SECURITY MANAGER CLASS = " + appsm.getClass()); + } + } + @ClassRule public static CustomSqlUnit sqlUnit = new CustomSqlUnit( Arrays.asList("sql/schema-ts.sql", "sql/schema-entities.sql", "sql/system-data.sql"), diff --git a/application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java b/application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java index 3ddcdf752e..76145c954d 100644 --- a/application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java +++ b/application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java @@ -29,6 +29,14 @@ import java.util.Arrays; @ClasspathSuite.ClassnameFilters({"org.thingsboard.server.system.sql.*SqlTest"}) public class SystemSqlTestSuite { + static { + SecurityManager appsm = System.getSecurityManager(); + System.out.println("SECURITY MANAGER = " + appsm); + if (appsm != null) { + System.out.println("SECURITY MANAGER CLASS = " + appsm.getClass()); + } + } + @ClassRule public static CustomSqlUnit sqlUnit = new CustomSqlUnit( Arrays.asList("sql/schema-ts.sql", "sql/schema-entities.sql", "sql/system-data.sql"), From 0d5f09a9fa2dda522cba5503fcdc856bc729d78e Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 6 Mar 2019 16:29:48 +0200 Subject: [PATCH 16/19] Diagnostic output --- .../server/system/SystemSqlTestSuite.java | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java b/application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java index 76145c954d..ae45e35fa9 100644 --- a/application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java +++ b/application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java @@ -15,11 +15,21 @@ */ package org.thingsboard.server.system; +import net.bytebuddy.asm.AsmVisitorWrapper; +import net.bytebuddy.asm.MemberSubstitution; +import net.bytebuddy.matcher.ElementMatchers; +import org.apache.cassandra.cql3.functions.ThreadAwareSecurityManager; +import org.hibernate.HibernateException; +import org.hibernate.bytecode.internal.bytebuddy.ByteBuddyState; +import org.hibernate.bytecode.internal.bytebuddy.HibernateMethodLookupDispatcher; import org.junit.ClassRule; import org.junit.extensions.cpsuite.ClasspathSuite; import org.junit.runner.RunWith; import org.thingsboard.server.dao.CustomSqlUnit; +import java.lang.reflect.Method; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.Arrays; /** @@ -30,11 +40,74 @@ import java.util.Arrays; public class SystemSqlTestSuite { static { + //ThreadAwareSecurityManager.install(); SecurityManager appsm = System.getSecurityManager(); System.out.println("SECURITY MANAGER = " + appsm); if (appsm != null) { System.out.println("SECURITY MANAGER CLASS = " + appsm.getClass()); } + + AsmVisitorWrapper.ForDeclaredMethods getDeclaredMethodMemberSubstitution; + AsmVisitorWrapper.ForDeclaredMethods getMethodMemberSubstitution; + + //if ( System.getSecurityManager() != null ) { + getDeclaredMethodMemberSubstitution = getDeclaredMethodMemberSubstitution(); + getMethodMemberSubstitution = getMethodMemberSubstitution(); + //} + //else { + // getDeclaredMethodMemberSubstitution = null; + // getMethodMemberSubstitution = null; + //} + + System.out.println("getDeclaredMethodMemberSubstitution = " + getDeclaredMethodMemberSubstitution); + System.out.println("getMethodMemberSubstitution = " + getMethodMemberSubstitution); + } + + private static class GetDeclaredMethodAction implements PrivilegedAction { + private final Class clazz; + private final String methodName; + private final Class[] parameterTypes; + + private GetDeclaredMethodAction(Class clazz, String methodName, Class... parameterTypes) { + this.clazz = clazz; + this.methodName = methodName; + this.parameterTypes = parameterTypes; + } + + @Override + public Method run() { + try { + Method method = clazz.getDeclaredMethod( methodName, parameterTypes ); + + return method; + } + catch (NoSuchMethodException e) { + throw new HibernateException( "Unable to prepare getDeclaredMethod()/getMethod() substitution", e ); + } + } + } + + + private static AsmVisitorWrapper.ForDeclaredMethods getDeclaredMethodMemberSubstitution() { + // this should only be called if the security manager is enabled, thus the privileged calls + return MemberSubstitution.relaxed() + .method( ElementMatchers.is( AccessController.doPrivileged( new SystemSqlTestSuite.GetDeclaredMethodAction( Class.class, + "getDeclaredMethod", String.class, Class[].class ) ) ) ) + .replaceWith( + AccessController.doPrivileged( new SystemSqlTestSuite.GetDeclaredMethodAction( HibernateMethodLookupDispatcher.class, + "getDeclaredMethod", Class.class, String.class, Class[].class ) ) ) + .on( ElementMatchers.isTypeInitializer() ); + } + + private static AsmVisitorWrapper.ForDeclaredMethods getMethodMemberSubstitution() { + // this should only be called if the security manager is enabled, thus the privileged calls + return MemberSubstitution.relaxed() + .method( ElementMatchers.is( AccessController.doPrivileged( new SystemSqlTestSuite.GetDeclaredMethodAction( Class.class, + "getMethod", String.class, Class[].class ) ) ) ) + .replaceWith( + AccessController.doPrivileged( new SystemSqlTestSuite.GetDeclaredMethodAction( HibernateMethodLookupDispatcher.class, + "getMethod", Class.class, String.class, Class[].class ) ) ) + .on( ElementMatchers.isTypeInitializer() ); } @ClassRule From 72453499afc45bd8d71a6f26942d65440065691f Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 6 Mar 2019 17:10:53 +0200 Subject: [PATCH 17/19] Remove security manager after nosql test completion. --- .../server/rules/RuleEngineSqlTestSuite.java | 8 --- .../server/system/SystemSqlTestSuite.java | 72 ------------------- .../server/dao/CustomCassandraCQLUnit.java | 1 + 3 files changed, 1 insertion(+), 80 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/rules/RuleEngineSqlTestSuite.java b/application/src/test/java/org/thingsboard/server/rules/RuleEngineSqlTestSuite.java index d4b04289a8..c49a029dc4 100644 --- a/application/src/test/java/org/thingsboard/server/rules/RuleEngineSqlTestSuite.java +++ b/application/src/test/java/org/thingsboard/server/rules/RuleEngineSqlTestSuite.java @@ -28,14 +28,6 @@ import java.util.Arrays; "org.thingsboard.server.rules.lifecycle.sql.*Test"}) public class RuleEngineSqlTestSuite { - static { - SecurityManager appsm = System.getSecurityManager(); - System.out.println("SECURITY MANAGER = " + appsm); - if (appsm != null) { - System.out.println("SECURITY MANAGER CLASS = " + appsm.getClass()); - } - } - @ClassRule public static CustomSqlUnit sqlUnit = new CustomSqlUnit( Arrays.asList("sql/schema-ts.sql", "sql/schema-entities.sql", "sql/system-data.sql"), diff --git a/application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java b/application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java index ae45e35fa9..2fbf3814f7 100644 --- a/application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java +++ b/application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java @@ -15,21 +15,11 @@ */ package org.thingsboard.server.system; -import net.bytebuddy.asm.AsmVisitorWrapper; -import net.bytebuddy.asm.MemberSubstitution; -import net.bytebuddy.matcher.ElementMatchers; -import org.apache.cassandra.cql3.functions.ThreadAwareSecurityManager; -import org.hibernate.HibernateException; -import org.hibernate.bytecode.internal.bytebuddy.ByteBuddyState; -import org.hibernate.bytecode.internal.bytebuddy.HibernateMethodLookupDispatcher; import org.junit.ClassRule; import org.junit.extensions.cpsuite.ClasspathSuite; import org.junit.runner.RunWith; import org.thingsboard.server.dao.CustomSqlUnit; -import java.lang.reflect.Method; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.util.Arrays; /** @@ -46,68 +36,6 @@ public class SystemSqlTestSuite { if (appsm != null) { System.out.println("SECURITY MANAGER CLASS = " + appsm.getClass()); } - - AsmVisitorWrapper.ForDeclaredMethods getDeclaredMethodMemberSubstitution; - AsmVisitorWrapper.ForDeclaredMethods getMethodMemberSubstitution; - - //if ( System.getSecurityManager() != null ) { - getDeclaredMethodMemberSubstitution = getDeclaredMethodMemberSubstitution(); - getMethodMemberSubstitution = getMethodMemberSubstitution(); - //} - //else { - // getDeclaredMethodMemberSubstitution = null; - // getMethodMemberSubstitution = null; - //} - - System.out.println("getDeclaredMethodMemberSubstitution = " + getDeclaredMethodMemberSubstitution); - System.out.println("getMethodMemberSubstitution = " + getMethodMemberSubstitution); - } - - private static class GetDeclaredMethodAction implements PrivilegedAction { - private final Class clazz; - private final String methodName; - private final Class[] parameterTypes; - - private GetDeclaredMethodAction(Class clazz, String methodName, Class... parameterTypes) { - this.clazz = clazz; - this.methodName = methodName; - this.parameterTypes = parameterTypes; - } - - @Override - public Method run() { - try { - Method method = clazz.getDeclaredMethod( methodName, parameterTypes ); - - return method; - } - catch (NoSuchMethodException e) { - throw new HibernateException( "Unable to prepare getDeclaredMethod()/getMethod() substitution", e ); - } - } - } - - - private static AsmVisitorWrapper.ForDeclaredMethods getDeclaredMethodMemberSubstitution() { - // this should only be called if the security manager is enabled, thus the privileged calls - return MemberSubstitution.relaxed() - .method( ElementMatchers.is( AccessController.doPrivileged( new SystemSqlTestSuite.GetDeclaredMethodAction( Class.class, - "getDeclaredMethod", String.class, Class[].class ) ) ) ) - .replaceWith( - AccessController.doPrivileged( new SystemSqlTestSuite.GetDeclaredMethodAction( HibernateMethodLookupDispatcher.class, - "getDeclaredMethod", Class.class, String.class, Class[].class ) ) ) - .on( ElementMatchers.isTypeInitializer() ); - } - - private static AsmVisitorWrapper.ForDeclaredMethods getMethodMemberSubstitution() { - // this should only be called if the security manager is enabled, thus the privileged calls - return MemberSubstitution.relaxed() - .method( ElementMatchers.is( AccessController.doPrivileged( new SystemSqlTestSuite.GetDeclaredMethodAction( Class.class, - "getMethod", String.class, Class[].class ) ) ) ) - .replaceWith( - AccessController.doPrivileged( new SystemSqlTestSuite.GetDeclaredMethodAction( HibernateMethodLookupDispatcher.class, - "getMethod", Class.class, String.class, Class[].class ) ) ) - .on( ElementMatchers.isTypeInitializer() ); } @ClassRule diff --git a/dao/src/test/java/org/thingsboard/server/dao/CustomCassandraCQLUnit.java b/dao/src/test/java/org/thingsboard/server/dao/CustomCassandraCQLUnit.java index b57cd33c6c..c74dbd54e5 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/CustomCassandraCQLUnit.java +++ b/dao/src/test/java/org/thingsboard/server/dao/CustomCassandraCQLUnit.java @@ -82,6 +82,7 @@ public class CustomCassandraCQLUnit extends BaseCassandraUnit { session = null; cluster = null; } + System.setSecurityManager(null); } // Getters for those who do not like to directly access fields From 008b5dac47dd65cdc48593ef2ab8c68b84544a9e Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 6 Mar 2019 17:31:14 +0200 Subject: [PATCH 18/19] Cleanup --- .../thingsboard/server/system/SystemSqlTestSuite.java | 9 --------- pom.xml | 11 ----------- 2 files changed, 20 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java b/application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java index 2fbf3814f7..3ddcdf752e 100644 --- a/application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java +++ b/application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java @@ -29,15 +29,6 @@ import java.util.Arrays; @ClasspathSuite.ClassnameFilters({"org.thingsboard.server.system.sql.*SqlTest"}) public class SystemSqlTestSuite { - static { - //ThreadAwareSecurityManager.install(); - SecurityManager appsm = System.getSecurityManager(); - System.out.println("SECURITY MANAGER = " + appsm); - if (appsm != null) { - System.out.println("SECURITY MANAGER CLASS = " + appsm.getClass()); - } - } - @ClassRule public static CustomSqlUnit sqlUnit = new CustomSqlUnit( Arrays.asList("sql/schema-ts.sql", "sql/schema-entities.sql", "sql/system-data.sql"), diff --git a/pom.xml b/pom.xml index 8a6a9a7351..7f026e3943 100755 --- a/pom.xml +++ b/pom.xml @@ -87,7 +87,6 @@ 2.57 2.7.7 1.23 - 1.9.3 @@ -525,16 +524,6 @@ antlr ${antlr.version} - - net.bytebuddy - byte-buddy - ${bytebuddy.version} - - - net.bytebuddy - byte-buddy-agent - ${bytebuddy.version} - com.rabbitmq amqp-client From adb08a1d7b7679d051317081b680702efda807ce Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 7 Mar 2019 12:58:45 +0200 Subject: [PATCH 19/19] Actor message processors NPE fix. --- .../device/DeviceActorMessageProcessor.java | 22 +++--- .../RuleChainActorMessageProcessor.java | 70 ++++++++++--------- .../RuleNodeActorMessageProcessor.java | 13 ++-- .../AbstractMqttTelemetryIntegrationTest.java | 2 +- 4 files changed, 61 insertions(+), 46 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java index 0f40dd37f5..aa1f8bc909 100644 --- a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java @@ -118,17 +118,23 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { this.rpcSubscriptions = new HashMap<>(); this.toDeviceRpcPendingMap = new HashMap<>(); this.toServerRpcPendingMap = new HashMap<>(); - initAttributes(); - restoreSessions(); + if (initAttributes()) { + restoreSessions(); + } } - private void initAttributes() { + private boolean initAttributes() { Device device = systemContext.getDeviceService().findDeviceById(tenantId, deviceId); - this.deviceName = device.getName(); - this.deviceType = device.getType(); - this.defaultMetaData = new TbMsgMetaData(); - this.defaultMetaData.putValue("deviceName", deviceName); - this.defaultMetaData.putValue("deviceType", deviceType); + if (device != null) { + this.deviceName = device.getName(); + this.deviceType = device.getType(); + this.defaultMetaData = new TbMsgMetaData(); + this.defaultMetaData.putValue("deviceName", deviceName); + this.defaultMetaData.putValue("deviceType", deviceType); + return true; + } else { + return false; + } } void processRpcRequest(ActorContext context, ToDeviceRpcRequestActorMsg msg) { diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java index 368634eb06..44b6f3b6c5 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java @@ -91,17 +91,19 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor ruleNodeList = service.getRuleChainNodes(tenantId, entityId); - log.trace("[{}][{}] Starting rule chain with {} nodes", tenantId, entityId, ruleNodeList.size()); - // Creating and starting the actors; - for (RuleNode ruleNode : ruleNodeList) { - log.trace("[{}][{}] Creating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode); - ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode); - nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode)); + if (ruleChain != null) { + ruleChainName = ruleChain.getName(); + List ruleNodeList = service.getRuleChainNodes(tenantId, entityId); + log.trace("[{}][{}] Starting rule chain with {} nodes", tenantId, entityId, ruleNodeList.size()); + // Creating and starting the actors; + for (RuleNode ruleNode : ruleNodeList) { + log.trace("[{}][{}] Creating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode); + ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode); + nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode)); + } + initRoutes(ruleChain, ruleNodeList); + started = true; } - initRoutes(ruleChain, ruleNodeList); - started = true; } else { onUpdate(context); } @@ -110,31 +112,33 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor ruleNodeList = service.getRuleChainNodes(tenantId, entityId); - log.trace("[{}][{}] Updating rule chain with {} nodes", tenantId, entityId, ruleNodeList.size()); - for (RuleNode ruleNode : ruleNodeList) { - RuleNodeCtx existing = nodeActors.get(ruleNode.getId()); - if (existing == null) { - log.trace("[{}][{}] Creating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode); - ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode); - nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode)); - } else { - log.trace("[{}][{}] Updating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode); - existing.setSelf(ruleNode); - existing.getSelfActor().tell(new ComponentLifecycleMsg(tenantId, existing.getSelf().getId(), ComponentLifecycleEvent.UPDATED), self); + if (ruleChain != null) { + ruleChainName = ruleChain.getName(); + List ruleNodeList = service.getRuleChainNodes(tenantId, entityId); + log.trace("[{}][{}] Updating rule chain with {} nodes", tenantId, entityId, ruleNodeList.size()); + for (RuleNode ruleNode : ruleNodeList) { + RuleNodeCtx existing = nodeActors.get(ruleNode.getId()); + if (existing == null) { + log.trace("[{}][{}] Creating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode); + ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode); + nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode)); + } else { + log.trace("[{}][{}] Updating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode); + existing.setSelf(ruleNode); + existing.getSelfActor().tell(new ComponentLifecycleMsg(tenantId, existing.getSelf().getId(), ComponentLifecycleEvent.UPDATED), self); + } } + + Set existingNodes = ruleNodeList.stream().map(RuleNode::getId).collect(Collectors.toSet()); + List removedRules = nodeActors.keySet().stream().filter(node -> !existingNodes.contains(node)).collect(Collectors.toList()); + removedRules.forEach(ruleNodeId -> { + log.trace("[{}][{}] Removing rule node [{}]", tenantId, entityId, ruleNodeId); + RuleNodeCtx removed = nodeActors.remove(ruleNodeId); + removed.getSelfActor().tell(new ComponentLifecycleMsg(tenantId, removed.getSelf().getId(), ComponentLifecycleEvent.DELETED), self); + }); + + initRoutes(ruleChain, ruleNodeList); } - - Set existingNodes = ruleNodeList.stream().map(RuleNode::getId).collect(Collectors.toSet()); - List removedRules = nodeActors.keySet().stream().filter(node -> !existingNodes.contains(node)).collect(Collectors.toList()); - removedRules.forEach(ruleNodeId -> { - log.trace("[{}][{}] Removing rule node [{}]", tenantId, entityId, ruleNodeId); - RuleNodeCtx removed = nodeActors.remove(ruleNodeId); - removed.getSelfActor().tell(new ComponentLifecycleMsg(tenantId, removed.getSelf().getId(), ComponentLifecycleEvent.DELETED), self); - }); - - initRoutes(ruleChain, ruleNodeList); } @Override diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java index fc8ff3dc0a..a6543795ac 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java @@ -55,7 +55,9 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor componentClazz = Class.forName(ruleNode.getType()); - TbNode tbNode = (TbNode) (componentClazz.newInstance()); - tbNode.init(defaultCtx, new TbNodeConfiguration(ruleNode.getConfiguration())); + TbNode tbNode = null; + if (ruleNode != null) { + Class componentClazz = Class.forName(ruleNode.getType()); + tbNode = (TbNode) (componentClazz.newInstance()); + tbNode.init(defaultCtx, new TbNodeConfiguration(ruleNode.getConfiguration())); + } return tbNode; } diff --git a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/AbstractMqttTelemetryIntegrationTest.java b/application/src/test/java/org/thingsboard/server/mqtt/telemetry/AbstractMqttTelemetryIntegrationTest.java index 8e8639c50c..50de5727f9 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/AbstractMqttTelemetryIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/mqtt/telemetry/AbstractMqttTelemetryIntegrationTest.java @@ -111,7 +111,7 @@ public abstract class AbstractMqttTelemetryIntegrationTest extends AbstractContr client.subscribe("v1/devices/me/attributes", MqttQoS.AT_MOST_ONCE.value()); String payload = "{\"key\":\"value\"}"; String result = doPostAsync("/api/plugins/telemetry/" + savedDevice.getId() + "/SHARED_SCOPE", payload, String.class, status().isOk()); - latch.await(3, TimeUnit.SECONDS); + latch.await(10, TimeUnit.SECONDS); assertEquals(payload, callback.getPayload()); assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS()); }