Browse Source

DAO layer, services, configuration and UI

pull/1/head
Igor Kulikov 10 years ago
parent
commit
5fdbb51cd7
  1. 75
      application/src/main/java/org/thingsboard/server/config/JwtSettings.java
  2. 34
      application/src/main/java/org/thingsboard/server/config/ThingsboardMessageConfiguration.java
  3. 161
      application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java
  4. 29
      application/src/main/java/org/thingsboard/server/config/WebConfig.java
  5. 96
      application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java
  6. 75
      application/src/main/java/org/thingsboard/server/controller/AdminController.java
  7. 236
      application/src/main/java/org/thingsboard/server/controller/AuthController.java
  8. 377
      application/src/main/java/org/thingsboard/server/controller/BaseController.java
  9. 66
      application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java
  10. 87
      application/src/main/java/org/thingsboard/server/controller/CustomerController.java
  11. 148
      application/src/main/java/org/thingsboard/server/controller/DashboardController.java
  12. 176
      application/src/main/java/org/thingsboard/server/controller/DeviceController.java
  13. 116
      application/src/main/java/org/thingsboard/server/controller/EventController.java
  14. 197
      application/src/main/java/org/thingsboard/server/controller/PluginController.java
  15. 193
      application/src/main/java/org/thingsboard/server/controller/RuleController.java
  16. 89
      application/src/main/java/org/thingsboard/server/controller/TenantController.java
  17. 179
      application/src/main/java/org/thingsboard/server/controller/UserController.java
  18. 118
      application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java
  19. 116
      application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java
  20. 108
      application/src/main/java/org/thingsboard/server/controller/plugin/PluginApiController.java
  21. 30
      application/src/main/java/org/thingsboard/server/controller/plugin/PluginNotFoundException.java
  22. 203
      application/src/main/java/org/thingsboard/server/controller/plugin/PluginWebSocketHandler.java
  23. 28
      application/src/main/java/org/thingsboard/server/controller/plugin/PluginWebSocketMsgEndpoint.java
  24. 41
      application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorCode.java
  25. 60
      application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponse.java
  26. 133
      application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java
  27. 51
      application/src/main/java/org/thingsboard/server/exception/ThingsboardException.java
  28. 39
      application/src/main/java/org/thingsboard/server/service/environment/EnvironmentLogService.java
  29. 208
      application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java
  30. 36
      application/src/main/java/org/thingsboard/server/service/mail/MailService.java
  31. 66
      application/src/main/java/org/thingsboard/server/service/security/auth/AbstractJwtAuthenticationToken.java
  32. 32
      application/src/main/java/org/thingsboard/server/service/security/auth/JwtAuthenticationToken.java
  33. 32
      application/src/main/java/org/thingsboard/server/service/security/auth/RefreshAuthenticationToken.java
  34. 58
      application/src/main/java/org/thingsboard/server/service/security/auth/jwt/JwtAuthenticationProvider.java
  35. 71
      application/src/main/java/org/thingsboard/server/service/security/auth/jwt/JwtTokenAuthenticationProcessingFilter.java
  36. 79
      application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenAuthenticationProvider.java
  37. 94
      application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenProcessingFilter.java
  38. 38
      application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenRepository.java
  39. 32
      application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenRequest.java
  40. 45
      application/src/main/java/org/thingsboard/server/service/security/auth/jwt/SkipPathRequestMatcher.java
  41. 42
      application/src/main/java/org/thingsboard/server/service/security/auth/jwt/extractor/JwtHeaderTokenExtractor.java
  42. 43
      application/src/main/java/org/thingsboard/server/service/security/auth/jwt/extractor/JwtQueryTokenExtractor.java
  43. 22
      application/src/main/java/org/thingsboard/server/service/security/auth/jwt/extractor/TokenExtractor.java
  44. 38
      application/src/main/java/org/thingsboard/server/service/security/auth/rest/LoginRequest.java
  45. 79
      application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAuthenticationProvider.java
  46. 44
      application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAwareAuthenticationFailureHandler.java
  47. 85
      application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAwareAuthenticationSuccessHandler.java
  48. 93
      application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestLoginProcessingFilter.java
  49. 70
      application/src/main/java/org/thingsboard/server/service/security/device/DefaultDeviceAuthService.java
  50. 26
      application/src/main/java/org/thingsboard/server/service/security/exception/AuthMethodNotSupportedException.java
  51. 38
      application/src/main/java/org/thingsboard/server/service/security/exception/JwtExpiredTokenException.java
  52. 64
      application/src/main/java/org/thingsboard/server/service/security/model/SecurityUser.java
  53. 38
      application/src/main/java/org/thingsboard/server/service/security/model/token/AccessJwtToken.java
  54. 20
      application/src/main/java/org/thingsboard/server/service/security/model/token/JwtToken.java
  55. 158
      application/src/main/java/org/thingsboard/server/service/security/model/token/JwtTokenFactory.java
  56. 56
      application/src/main/java/org/thingsboard/server/service/security/model/token/RawAccessJwtToken.java
  57. 176
      dao/pom.xml
  58. 93
      dao/src/main/java/org/thingsboard/server/dao/AbstractDao.java
  59. 114
      dao/src/main/java/org/thingsboard/server/dao/AbstractModelDao.java
  60. 78
      dao/src/main/java/org/thingsboard/server/dao/AbstractSearchTextDao.java
  61. 90
      dao/src/main/java/org/thingsboard/server/dao/AbstractSearchTimeDao.java
  62. 33
      dao/src/main/java/org/thingsboard/server/dao/Dao.java
  63. 61
      dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java
  64. 159
      dao/src/main/java/org/thingsboard/server/dao/cassandra/CassandraCluster.java
  65. 74
      dao/src/main/java/org/thingsboard/server/dao/cassandra/CassandraQueryOptions.java
  66. 75
      dao/src/main/java/org/thingsboard/server/dao/cassandra/CassandraSocketOptions.java
  67. 169
      dao/src/main/java/org/thingsboard/server/dao/component/BaseComponentDescriptorDao.java
  68. 133
      dao/src/main/java/org/thingsboard/server/dao/component/BaseComponentDescriptorService.java
  69. 48
      dao/src/main/java/org/thingsboard/server/dao/component/ComponentDescriptorDao.java
  70. 45
      dao/src/main/java/org/thingsboard/server/dao/component/ComponentDescriptorService.java
  71. 48
      dao/src/main/java/org/thingsboard/server/dao/customer/CustomerDao.java
  72. 63
      dao/src/main/java/org/thingsboard/server/dao/customer/CustomerDaoImpl.java
  73. 36
      dao/src/main/java/org/thingsboard/server/dao/customer/CustomerService.java
  74. 145
      dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java
  75. 60
      dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDao.java
  76. 83
      dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDaoImpl.java
  77. 45
      dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java
  78. 195
      dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java
  79. 55
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsDao.java
  80. 77
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsDaoImpl.java
  81. 39
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsService.java
  82. 134
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java
  83. 68
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java
  84. 85
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceDaoImpl.java
  85. 44
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceService.java
  86. 231
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
  87. 136
      dao/src/main/java/org/thingsboard/server/dao/event/BaseEventDao.java
  88. 130
      dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java
  89. 82
      dao/src/main/java/org/thingsboard/server/dao/event/EventDao.java
  90. 38
      dao/src/main/java/org/thingsboard/server/dao/event/EventService.java
  91. 29
      dao/src/main/java/org/thingsboard/server/dao/exception/DataValidationException.java
  92. 38
      dao/src/main/java/org/thingsboard/server/dao/exception/DatabaseException.java
  93. 30
      dao/src/main/java/org/thingsboard/server/dao/exception/IncorrectParameterException.java
  94. 147
      dao/src/main/java/org/thingsboard/server/dao/model/AdminSettingsEntity.java
  95. 27
      dao/src/main/java/org/thingsboard/server/dao/model/BaseEntity.java
  96. 162
      dao/src/main/java/org/thingsboard/server/dao/model/ComponentDescriptorEntity.java
  97. 365
      dao/src/main/java/org/thingsboard/server/dao/model/CustomerEntity.java
  98. 221
      dao/src/main/java/org/thingsboard/server/dao/model/DashboardEntity.java
  99. 186
      dao/src/main/java/org/thingsboard/server/dao/model/DeviceCredentialsEntity.java
  100. 214
      dao/src/main/java/org/thingsboard/server/dao/model/DeviceEntity.java

75
application/src/main/java/org/thingsboard/server/config/JwtSettings.java

@ -0,0 +1,75 @@
/**
* Copyright © 2016 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.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.thingsboard.server.service.security.model.token.JwtToken;
@Configuration
@ConfigurationProperties(prefix = "security.jwt")
public class JwtSettings {
/**
* {@link JwtToken} will expire after this time.
*/
private Integer tokenExpirationTime;
/**
* Token issuer.
*/
private String tokenIssuer;
/**
* Key is used to sign {@link JwtToken}.
*/
private String tokenSigningKey;
/**
* {@link JwtToken} can be refreshed during this timeframe.
*/
private Integer refreshTokenExpTime;
public Integer getRefreshTokenExpTime() {
return refreshTokenExpTime;
}
public void setRefreshTokenExpTime(Integer refreshTokenExpTime) {
this.refreshTokenExpTime = refreshTokenExpTime;
}
public Integer getTokenExpirationTime() {
return tokenExpirationTime;
}
public void setTokenExpirationTime(Integer tokenExpirationTime) {
this.tokenExpirationTime = tokenExpirationTime;
}
public String getTokenIssuer() {
return tokenIssuer;
}
public void setTokenIssuer(String tokenIssuer) {
this.tokenIssuer = tokenIssuer;
}
public String getTokenSigningKey() {
return tokenSigningKey;
}
public void setTokenSigningKey(String tokenSigningKey) {
this.tokenSigningKey = tokenSigningKey;
}
}

34
application/src/main/java/org/thingsboard/server/config/ThingsboardMessageConfiguration.java

@ -0,0 +1,34 @@
/**
* Copyright © 2016 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.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
@Configuration
public class ThingsboardMessageConfiguration {
@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("i18n/messages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
}

161
application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java

@ -0,0 +1,161 @@
/**
* Copyright © 2016 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 com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.thingsboard.server.exception.ThingsboardErrorResponseHandler;
import org.thingsboard.server.service.security.auth.rest.RestAuthenticationProvider;
import org.thingsboard.server.service.security.auth.rest.RestLoginProcessingFilter;
import org.thingsboard.server.service.security.auth.jwt.*;
import org.thingsboard.server.service.security.auth.jwt.extractor.TokenExtractor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapter {
public static final String JWT_TOKEN_HEADER_PARAM = "X-Authorization";
public static final String JWT_TOKEN_QUERY_PARAM = "token";
public static final String DEVICE_API_ENTRY_POINT = "/api/v1/**";
public static final String FORM_BASED_LOGIN_ENTRY_POINT = "/api/auth/login";
public static final String TOKEN_REFRESH_ENTRY_POINT = "/api/auth/token";
public static final String[] NON_TOKEN_BASED_AUTH_ENTRY_POINTS = new String[] {"/index.html", "/static/**", "/api/noauth/**"};
public static final String TOKEN_BASED_AUTH_ENTRY_POINT = "/api/**";
public static final String WS_TOKEN_BASED_AUTH_ENTRY_POINT = "/api/ws/**";
@Autowired private ThingsboardErrorResponseHandler restAccessDeniedHandler;
@Autowired private AuthenticationSuccessHandler successHandler;
@Autowired private AuthenticationFailureHandler failureHandler;
@Autowired private RestAuthenticationProvider restAuthenticationProvider;
@Autowired private JwtAuthenticationProvider jwtAuthenticationProvider;
@Autowired private RefreshTokenAuthenticationProvider refreshTokenAuthenticationProvider;
@Autowired
@Qualifier("jwtHeaderTokenExtractor")
private TokenExtractor jwtHeaderTokenExtractor;
@Autowired
@Qualifier("jwtQueryTokenExtractor")
private TokenExtractor jwtQueryTokenExtractor;
@Autowired private AuthenticationManager authenticationManager;
@Autowired private ObjectMapper objectMapper;
@Bean
protected RestLoginProcessingFilter buildRestLoginProcessingFilter() throws Exception {
RestLoginProcessingFilter filter = new RestLoginProcessingFilter(FORM_BASED_LOGIN_ENTRY_POINT, successHandler, failureHandler, objectMapper);
filter.setAuthenticationManager(this.authenticationManager);
return filter;
}
@Bean
protected JwtTokenAuthenticationProcessingFilter buildJwtTokenAuthenticationProcessingFilter() throws Exception {
List<String> pathsToSkip = new ArrayList(Arrays.asList(NON_TOKEN_BASED_AUTH_ENTRY_POINTS));
pathsToSkip.addAll(Arrays.asList(WS_TOKEN_BASED_AUTH_ENTRY_POINT, TOKEN_REFRESH_ENTRY_POINT, FORM_BASED_LOGIN_ENTRY_POINT, DEVICE_API_ENTRY_POINT));
SkipPathRequestMatcher matcher = new SkipPathRequestMatcher(pathsToSkip, TOKEN_BASED_AUTH_ENTRY_POINT);
JwtTokenAuthenticationProcessingFilter filter
= new JwtTokenAuthenticationProcessingFilter(failureHandler, jwtHeaderTokenExtractor, matcher);
filter.setAuthenticationManager(this.authenticationManager);
return filter;
}
@Bean
protected RefreshTokenProcessingFilter buildRefreshTokenProcessingFilter() throws Exception {
RefreshTokenProcessingFilter filter = new RefreshTokenProcessingFilter(TOKEN_REFRESH_ENTRY_POINT, successHandler, failureHandler, objectMapper);
filter.setAuthenticationManager(this.authenticationManager);
return filter;
}
@Bean
protected JwtTokenAuthenticationProcessingFilter buildWsJwtTokenAuthenticationProcessingFilter() throws Exception {
AntPathRequestMatcher matcher = new AntPathRequestMatcher(WS_TOKEN_BASED_AUTH_ENTRY_POINT);
JwtTokenAuthenticationProcessingFilter filter
= new JwtTokenAuthenticationProcessingFilter(failureHandler, jwtQueryTokenExtractor, matcher);
filter.setAuthenticationManager(this.authenticationManager);
return filter;
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(restAuthenticationProvider);
auth.authenticationProvider(jwtAuthenticationProvider);
auth.authenticationProvider(refreshTokenAuthenticationProvider);
}
@Bean
protected BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.headers().frameOptions().disable()
.and()
.csrf().disable()
.exceptionHandling()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(DEVICE_API_ENTRY_POINT).permitAll() // Device HTTP Transport API
.antMatchers(FORM_BASED_LOGIN_ENTRY_POINT).permitAll() // Login end-point
.antMatchers(TOKEN_REFRESH_ENTRY_POINT).permitAll() // Token refresh end-point
.antMatchers(NON_TOKEN_BASED_AUTH_ENTRY_POINTS).permitAll() // static resources, user activation and password reset end-points
.and()
.authorizeRequests()
.antMatchers(WS_TOKEN_BASED_AUTH_ENTRY_POINT).authenticated() // Protected WebSocket API End-points
.antMatchers(TOKEN_BASED_AUTH_ENTRY_POINT).authenticated() // Protected API End-points
.and()
.exceptionHandling().accessDeniedHandler(restAccessDeniedHandler)
.and()
.addFilterBefore(buildRestLoginProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(buildJwtTokenAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(buildRefreshTokenProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(buildWsJwtTokenAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class);
}
}

29
application/src/main/java/org/thingsboard/server/config/WebConfig.java

@ -0,0 +1,29 @@
/**
* Copyright © 2016 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.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class WebConfig {
@RequestMapping(value = "/{path:^(?!api$)(?!static$)[^\\.]*}/**")
public String redirect() {
return "forward:/index.html";
}
}

96
application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java

@ -0,0 +1,96 @@
/**
* Copyright © 2016 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 java.util.Map;
import org.thingsboard.server.exception.ThingsboardErrorCode;
import org.thingsboard.server.exception.ThingsboardException;
import org.thingsboard.server.controller.plugin.PluginWebSocketHandler;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.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;
@Configuration
@EnableWebSocket
public class WebSocketConfiguration implements WebSocketConfigurer {
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
public ServletServerContainerFactoryBean createWebSocketContainer() {
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
container.setMaxTextMessageBufferSize(8192);
container.setMaxBinaryMessageBufferSize(8192);
return container;
}
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(pluginWsHandler(), WS_PLUGIN_MAPPING).setAllowedOrigins("*")
.addInterceptors(new HttpSessionHandshakeInterceptor(), new HandshakeInterceptor() {
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
Map<String, Object> attributes) throws Exception {
SecurityUser user = null;
try {
user = getCurrentUser();
} catch (ThingsboardException ex) {}
if (user == null) {
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return false;
} else {
attributes.put(WS_SECURITY_USER_ATTRIBUTE, user);
return true;
}
}
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
Exception exception) {
}
});
}
@Bean
public WebSocketHandler pluginWsHandler() {
return new PluginWebSocketHandler();
}
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);
}
}
}

75
application/src/main/java/org/thingsboard/server/controller/AdminController.java

@ -0,0 +1,75 @@
/**
* Copyright © 2016 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.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.thingsboard.server.common.data.AdminSettings;
import org.thingsboard.server.dao.settings.AdminSettingsService;
import org.thingsboard.server.exception.ThingsboardException;
import org.thingsboard.server.service.mail.MailService;
@RestController
@RequestMapping("/api/admin")
public class AdminController extends BaseController {
@Autowired
private MailService mailService;
@Autowired
private AdminSettingsService adminSettingsService;
@PreAuthorize("hasAuthority('SYS_ADMIN')")
@RequestMapping(value = "/settings/{key}", method = RequestMethod.GET)
@ResponseBody
public AdminSettings getAdminSettings(@PathVariable("key") String key) throws ThingsboardException {
try {
return checkNotNull(adminSettingsService.findAdminSettingsByKey(key));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('SYS_ADMIN')")
@RequestMapping(value = "/settings", method = RequestMethod.POST)
@ResponseBody
public AdminSettings saveAdminSettings(@RequestBody AdminSettings adminSettings) throws ThingsboardException {
try {
adminSettings = checkNotNull(adminSettingsService.saveAdminSettings(adminSettings));
if (adminSettings.getKey().equals("mail")) {
mailService.updateMailConfiguration();
}
return adminSettings;
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('SYS_ADMIN')")
@RequestMapping(value = "/settings/testMail", method = RequestMethod.POST)
public void sendTestMail(@RequestBody AdminSettings adminSettings) throws ThingsboardException {
try {
adminSettings = checkNotNull(adminSettings);
if (adminSettings.getKey().equals("mail")) {
String email = getCurrentUser().getEmail();
mailService.sendTestMail(adminSettings.getJsonValue(), email);
}
} catch (Exception e) {
throw handleException(e);
}
}
}

236
application/src/main/java/org/thingsboard/server/controller/AuthController.java

@ -0,0 +1,236 @@
/**
* Copyright © 2016 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.controller;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.*;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.security.UserCredentials;
import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.exception.ThingsboardErrorCode;
import org.thingsboard.server.exception.ThingsboardException;
import org.thingsboard.server.service.mail.MailService;
import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.model.token.JwtToken;
import org.thingsboard.server.service.security.model.token.JwtTokenFactory;
import javax.servlet.http.HttpServletRequest;
import java.net.URI;
import java.net.URISyntaxException;
@RestController
@RequestMapping("/api")
@Slf4j
public class AuthController extends BaseController {
@Autowired
private BCryptPasswordEncoder passwordEncoder;
@Autowired
private JwtTokenFactory tokenFactory;
@Autowired
private RefreshTokenRepository refreshTokenRepository;
@Autowired
private UserService userService;
@Autowired
private MailService mailService;
@PreAuthorize("isAuthenticated()")
@RequestMapping(value = "/auth/user", method = RequestMethod.GET)
public @ResponseBody User getUser() throws ThingsboardException {
try {
SecurityUser securityUser = getCurrentUser();
return userService.findUserById(securityUser.getId());
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("isAuthenticated()")
@RequestMapping(value = "/auth/changePassword", method = RequestMethod.POST)
@ResponseStatus(value = HttpStatus.OK)
public void changePassword (
@RequestParam(value = "currentPassword") String currentPassword,
@RequestParam(value = "newPassword") String newPassword) throws ThingsboardException {
try {
SecurityUser securityUser = getCurrentUser();
UserCredentials userCredentials = userService.findUserCredentialsByUserId(securityUser.getId());
if (!passwordEncoder.matches(currentPassword, userCredentials.getPassword())) {
throw new ThingsboardException("Current password doesn't match!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
}
userCredentials.setPassword(passwordEncoder.encode(newPassword));
userService.saveUserCredentials(userCredentials);
} catch (Exception e) {
throw handleException(e);
}
}
@RequestMapping(value = "/noauth/activate", params = { "activateToken" }, method = RequestMethod.GET)
public ResponseEntity<String> checkActivateToken(
@RequestParam(value = "activateToken") String activateToken) {
HttpHeaders headers = new HttpHeaders();
HttpStatus responseStatus;
UserCredentials userCredentials = userService.findUserCredentialsByActivateToken(activateToken);
if (userCredentials != null) {
String createPasswordURI = "/login/createPassword";
try {
URI location = new URI(createPasswordURI + "?activateToken=" + activateToken);
headers.setLocation(location);
responseStatus = HttpStatus.PERMANENT_REDIRECT;
} catch (URISyntaxException e) {
log.error("Unable to create URI with address [{}]", createPasswordURI);
responseStatus = HttpStatus.BAD_REQUEST;
}
} else {
responseStatus = HttpStatus.CONFLICT;
}
return new ResponseEntity<>(headers, responseStatus);
}
@RequestMapping(value = "/noauth/resetPasswordByEmail", method = RequestMethod.POST)
@ResponseStatus(value = HttpStatus.OK)
public void requestResetPasswordByEmail (
@RequestParam(value = "email") String email,
HttpServletRequest request) throws ThingsboardException {
try {
UserCredentials userCredentials = userService.requestPasswordReset(email);
String baseUrl = String.format("%s://%s:%d",
request.getScheme(),
request.getServerName(),
request.getServerPort());
String resetPasswordUrl = String.format("%s/api/noauth/resetPassword?resetToken=%s", baseUrl,
userCredentials.getResetToken());
mailService.sendResetPasswordEmail(resetPasswordUrl, email);
} catch (Exception e) {
throw handleException(e);
}
}
@RequestMapping(value = "/noauth/resetPassword", params = { "resetToken" }, method = RequestMethod.GET)
public ResponseEntity<String> checkResetToken(
@RequestParam(value = "resetToken") String resetToken) {
HttpHeaders headers = new HttpHeaders();
HttpStatus responseStatus;
String resetPasswordURI = "/login/resetPassword";
UserCredentials userCredentials = userService.findUserCredentialsByResetToken(resetToken);
if (userCredentials != null) {
try {
URI location = new URI(resetPasswordURI + "?resetToken=" + resetToken);
headers.setLocation(location);
responseStatus = HttpStatus.PERMANENT_REDIRECT;
} catch (URISyntaxException e) {
log.error("Unable to create URI with address [{}]", resetPasswordURI);
responseStatus = HttpStatus.BAD_REQUEST;
}
} else {
responseStatus = HttpStatus.CONFLICT;
}
return new ResponseEntity<>(headers, responseStatus);
}
@RequestMapping(value = "/noauth/activate", method = RequestMethod.POST)
@ResponseStatus(value = HttpStatus.OK)
@ResponseBody
public JsonNode activateUser(
@RequestParam(value = "activateToken") String activateToken,
@RequestParam(value = "password") String password,
HttpServletRequest request) throws ThingsboardException {
try {
String encodedPassword = passwordEncoder.encode(password);
UserCredentials credentials = userService.activateUserCredentials(activateToken, encodedPassword);
User user = userService.findUserById(credentials.getUserId());
SecurityUser securityUser = new SecurityUser(user, credentials.isEnabled());
String baseUrl = String.format("%s://%s:%d",
request.getScheme(),
request.getServerName(),
request.getServerPort());
String loginUrl = String.format("%s/login", baseUrl);
String email = user.getEmail();
mailService.sendAccountActivatedEmail(loginUrl, email);
JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser);
JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser);
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode tokenObject = objectMapper.createObjectNode();
tokenObject.put("token", accessToken.getToken());
tokenObject.put("refreshToken", refreshToken.getToken());
return tokenObject;
} catch (Exception e) {
throw handleException(e);
}
}
@RequestMapping(value = "/noauth/resetPassword", method = RequestMethod.POST)
@ResponseStatus(value = HttpStatus.OK)
@ResponseBody
public JsonNode resetPassword(
@RequestParam(value = "resetToken") String resetToken,
@RequestParam(value = "password") String password,
HttpServletRequest request) throws ThingsboardException {
try {
UserCredentials userCredentials = userService.findUserCredentialsByResetToken(resetToken);
if (userCredentials != null) {
String encodedPassword = passwordEncoder.encode(password);
userCredentials.setPassword(encodedPassword);
userCredentials.setResetToken(null);
userCredentials = userService.saveUserCredentials(userCredentials);
User user = userService.findUserById(userCredentials.getUserId());
SecurityUser securityUser = new SecurityUser(user, userCredentials.isEnabled());
String baseUrl = String.format("%s://%s:%d",
request.getScheme(),
request.getServerName(),
request.getServerPort());
String loginUrl = String.format("%s/login", baseUrl);
String email = user.getEmail();
mailService.sendPasswordWasResetEmail(loginUrl, email);
JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser);
JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser);
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode tokenObject = objectMapper.createObjectNode();
tokenObject.put("token", accessToken.getToken());
tokenObject.put("refreshToken", refreshToken.getToken());
return tokenObject;
} else {
throw new ThingsboardException("Invalid reset token!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
}
} catch (Exception e) {
throw handleException(e);
}
}
}

377
application/src/main/java/org/thingsboard/server/controller/BaseController.java

@ -0,0 +1,377 @@
/**
* Copyright © 2016 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.controller;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.thingsboard.server.actors.service.ActorService;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.*;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.data.plugin.PluginMetaData;
import org.thingsboard.server.common.data.rule.RuleMetaData;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.widget.WidgetType;
import org.thingsboard.server.common.data.widget.WidgetsBundle;
import org.thingsboard.server.dao.customer.CustomerService;
import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.device.DeviceCredentialsService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.plugin.PluginService;
import org.thingsboard.server.dao.rule.RuleService;
import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.dao.widget.WidgetTypeService;
import org.thingsboard.server.dao.widget.WidgetsBundleService;
import org.thingsboard.server.exception.ThingsboardErrorCode;
import org.thingsboard.server.exception.ThingsboardErrorResponseHandler;
import org.thingsboard.server.exception.ThingsboardException;
import org.thingsboard.server.service.component.ComponentDiscoveryService;
import org.thingsboard.server.service.security.model.SecurityUser;
import javax.mail.MessagingException;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import static org.thingsboard.server.dao.service.Validator.validateId;
@Slf4j
public abstract class BaseController {
@Autowired
private ThingsboardErrorResponseHandler errorResponseHandler;
@Autowired
protected CustomerService customerService;
@Autowired
protected UserService userService;
@Autowired
protected DeviceService deviceService;
@Autowired
protected DeviceCredentialsService deviceCredentialsService;
@Autowired
protected WidgetsBundleService widgetsBundleService;
@Autowired
protected WidgetTypeService widgetTypeService;
@Autowired
protected DashboardService dashboardService;
@Autowired
protected ComponentDiscoveryService componentDescriptorService;
@Autowired
protected RuleService ruleService;
@Autowired
protected PluginService pluginService;
@Autowired
protected ActorService actorService;
@ExceptionHandler(ThingsboardException.class)
public void handleThingsboardException(ThingsboardException ex, HttpServletResponse response) {
errorResponseHandler.handle(ex, response);
}
ThingsboardException handleException(Exception exception) {
return handleException(exception, true);
}
private ThingsboardException handleException(Exception exception, boolean logException) {
if (logException) {
log.error("Error [{}]", exception.getMessage());
}
String cause = "";
if (exception.getCause() != null) {
cause = exception.getCause().getClass().getCanonicalName();
}
if (exception instanceof ThingsboardException) {
return (ThingsboardException) exception;
} else if (exception instanceof IllegalArgumentException || exception instanceof IncorrectParameterException
|| exception instanceof DataValidationException || cause.contains("IncorrectParameterException")) {
return new ThingsboardException(exception.getMessage(), ThingsboardErrorCode.BAD_REQUEST_PARAMS);
} else if (exception instanceof MessagingException) {
return new ThingsboardException("Unable to send mail: " + exception.getMessage(), ThingsboardErrorCode.GENERAL);
} else {
return new ThingsboardException(exception.getMessage(), ThingsboardErrorCode.GENERAL);
}
}
<T> T checkNotNull(T reference) throws ThingsboardException {
if (reference == null) {
throw new ThingsboardException("Requested item wasn't found!", ThingsboardErrorCode.ITEM_NOT_FOUND);
}
return reference;
}
<T> T checkNotNull(Optional<T> reference) throws ThingsboardException {
if (reference.isPresent()) {
return reference.get();
} else {
throw new ThingsboardException("Requested item wasn't found!", ThingsboardErrorCode.ITEM_NOT_FOUND);
}
}
void checkParameter(String name, String param) throws ThingsboardException {
if (StringUtils.isEmpty(param)) {
throw new ThingsboardException("Parameter '" + name + "' can't be empty!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
}
}
UUID toUUID(String id) {
return UUID.fromString(id);
}
TimePageLink createPageLink(int limit, Long startTime, Long endTime, boolean ascOrder, String idOffset) {
UUID idOffsetUuid = null;
if (StringUtils.isNotEmpty(idOffset)) {
idOffsetUuid = toUUID(idOffset);
}
return new TimePageLink(limit, startTime, endTime, ascOrder, idOffsetUuid);
}
TextPageLink createPageLink(int limit, String textSearch, String idOffset, String textOffset) {
UUID idOffsetUuid = null;
if (StringUtils.isNotEmpty(idOffset)) {
idOffsetUuid = toUUID(idOffset);
}
return new TextPageLink(limit, textSearch, idOffsetUuid, textOffset);
}
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);
}
}
void checkTenantId(TenantId tenantId) throws ThingsboardException {
validateId(tenantId, "Incorrect tenantId " + tenantId);
SecurityUser authUser = getCurrentUser();
if (authUser.getAuthority() != Authority.SYS_ADMIN &&
(authUser.getTenantId() == null || !authUser.getTenantId().equals(tenantId))) {
throw new ThingsboardException("You don't have permission to perform this operation!",
ThingsboardErrorCode.PERMISSION_DENIED);
}
}
protected TenantId getTenantId() throws ThingsboardException {
return getCurrentUser().getTenantId();
}
Customer checkCustomerId(CustomerId customerId) throws ThingsboardException {
try {
validateId(customerId, "Incorrect customerId " + customerId);
SecurityUser authUser = getCurrentUser();
if (authUser.getAuthority() == Authority.SYS_ADMIN ||
(authUser.getAuthority() != Authority.TENANT_ADMIN &&
(authUser.getCustomerId() == null || !authUser.getCustomerId().equals(customerId)))) {
throw new ThingsboardException("You don't have permission to perform this operation!",
ThingsboardErrorCode.PERMISSION_DENIED);
}
Customer customer = customerService.findCustomerById(customerId);
checkCustomer(customer);
return customer;
} catch (Exception e) {
throw handleException(e, false);
}
}
private void checkCustomer(Customer customer) throws ThingsboardException {
checkNotNull(customer);
checkTenantId(customer.getTenantId());
}
User checkUserId(UserId userId) throws ThingsboardException {
try {
validateId(userId, "Incorrect userId " + userId);
User user = userService.findUserById(userId);
checkUser(user);
return user;
} catch (Exception e) {
throw handleException(e, false);
}
}
private void checkUser(User user) throws ThingsboardException {
checkNotNull(user);
checkTenantId(user.getTenantId());
if (user.getAuthority() == Authority.CUSTOMER_USER) {
checkCustomerId(user.getCustomerId());
}
}
Device checkDeviceId(DeviceId deviceId) throws ThingsboardException {
try {
validateId(deviceId, "Incorrect deviceId " + deviceId);
Device device = deviceService.findDeviceById(deviceId);
checkDevice(device);
return device;
} catch (Exception e) {
throw handleException(e, false);
}
}
private void checkDevice(Device device) throws ThingsboardException {
checkNotNull(device);
checkTenantId(device.getTenantId());
if (device.getCustomerId() != null && !device.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) {
checkCustomerId(device.getCustomerId());
}
}
WidgetsBundle checkWidgetsBundleId(WidgetsBundleId widgetsBundleId, boolean modify) throws ThingsboardException {
try {
validateId(widgetsBundleId, "Incorrect widgetsBundleId " + widgetsBundleId);
WidgetsBundle widgetsBundle = widgetsBundleService.findWidgetsBundleById(widgetsBundleId);
checkWidgetsBundle(widgetsBundle, modify);
return widgetsBundle;
} catch (Exception e) {
throw handleException(e, false);
}
}
private void checkWidgetsBundle(WidgetsBundle widgetsBundle, boolean modify) throws ThingsboardException {
checkNotNull(widgetsBundle);
if (widgetsBundle.getTenantId() != null && !widgetsBundle.getTenantId().getId().equals(ModelConstants.NULL_UUID)) {
checkTenantId(widgetsBundle.getTenantId());
} else if (modify && getCurrentUser().getAuthority() != Authority.SYS_ADMIN) {
throw new ThingsboardException("You don't have permission to perform this operation!",
ThingsboardErrorCode.PERMISSION_DENIED);
}
}
WidgetType checkWidgetTypeId(WidgetTypeId widgetTypeId, boolean modify) throws ThingsboardException {
try {
validateId(widgetTypeId, "Incorrect widgetTypeId " + widgetTypeId);
WidgetType widgetType = widgetTypeService.findWidgetTypeById(widgetTypeId);
checkWidgetType(widgetType, modify);
return widgetType;
} catch (Exception e) {
throw handleException(e, false);
}
}
void checkWidgetType(WidgetType widgetType, boolean modify) throws ThingsboardException {
checkNotNull(widgetType);
if (widgetType.getTenantId() != null && !widgetType.getTenantId().getId().equals(ModelConstants.NULL_UUID)) {
checkTenantId(widgetType.getTenantId());
} else if (modify && getCurrentUser().getAuthority() != Authority.SYS_ADMIN) {
throw new ThingsboardException("You don't have permission to perform this operation!",
ThingsboardErrorCode.PERMISSION_DENIED);
}
}
Dashboard checkDashboardId(DashboardId dashboardId) throws ThingsboardException {
try {
validateId(dashboardId, "Incorrect dashboardId " + dashboardId);
Dashboard dashboard = dashboardService.findDashboardById(dashboardId);
checkDashboard(dashboard);
return dashboard;
} catch (Exception e) {
throw handleException(e, false);
}
}
private void checkDashboard(Dashboard dashboard) throws ThingsboardException {
checkNotNull(dashboard);
checkTenantId(dashboard.getTenantId());
if (dashboard.getCustomerId() != null && !dashboard.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) {
checkCustomerId(dashboard.getCustomerId());
}
}
ComponentDescriptor checkComponentDescriptorByClazz(String clazz) throws ThingsboardException {
try {
log.debug("[{}] Lookup component descriptor", clazz);
ComponentDescriptor componentDescriptor = checkNotNull(componentDescriptorService.getComponent(clazz));
return componentDescriptor;
} catch (Exception e) {
throw handleException(e, false);
}
}
List<ComponentDescriptor> checkComponentDescriptorsByType(ComponentType type) throws ThingsboardException {
try {
log.debug("[{}] Lookup component descriptors", type);
return componentDescriptorService.getComponents(type);
} catch (Exception e) {
throw handleException(e, false);
}
}
List<ComponentDescriptor> checkPluginActionsByPluginClazz(String pluginClazz) throws ThingsboardException {
try {
checkComponentDescriptorByClazz(pluginClazz);
log.debug("[{}] Lookup plugin actions", pluginClazz);
return componentDescriptorService.getPluginActions(pluginClazz);
} catch (Exception e) {
throw handleException(e, false);
}
}
protected PluginMetaData checkPlugin(PluginMetaData plugin) throws ThingsboardException {
checkNotNull(plugin);
SecurityUser authUser = getCurrentUser();
TenantId tenantId = plugin.getTenantId();
validateId(tenantId, "Incorrect tenantId " + tenantId);
if (authUser.getAuthority() != Authority.SYS_ADMIN) {
if (authUser.getTenantId() == null ||
!tenantId.getId().equals(ModelConstants.NULL_UUID) && !authUser.getTenantId().equals(tenantId)) {
throw new ThingsboardException("You don't have permission to perform this operation!",
ThingsboardErrorCode.PERMISSION_DENIED);
} else if (tenantId.getId().equals(ModelConstants.NULL_UUID)) {
plugin.setConfiguration(null);
}
}
return plugin;
}
protected RuleMetaData checkRule(RuleMetaData rule) throws ThingsboardException {
checkNotNull(rule);
checkTenantId(rule.getTenantId());
return rule;
}
}

66
application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java

@ -0,0 +1,66 @@
/**
* Copyright © 2016 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.controller;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.exception.ThingsboardException;
import java.util.List;
@RestController
@RequestMapping("/api")
public class ComponentDescriptorController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN')")
@RequestMapping(value = "/component/{componentDescriptorClazz:.+}", method = RequestMethod.GET)
@ResponseBody
public ComponentDescriptor getComponentDescriptorByClazz(@PathVariable("componentDescriptorClazz") String strComponentDescriptorClazz) throws ThingsboardException {
checkParameter("strComponentDescriptorClazz", strComponentDescriptorClazz);
try {
return checkComponentDescriptorByClazz(strComponentDescriptorClazz);
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN')")
@RequestMapping(value = "/components/{componentType}", method = RequestMethod.GET)
@ResponseBody
public List<ComponentDescriptor> getComponentDescriptorsByType(@PathVariable("componentType") String strComponentType) throws ThingsboardException {
checkParameter("componentType", strComponentType);
try {
return checkComponentDescriptorsByType(ComponentType.valueOf(strComponentType));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN')")
@RequestMapping(value = "/components/actions/{pluginClazz:.+}", method = RequestMethod.GET)
@ResponseBody
public List<ComponentDescriptor> getPluginActionsByPluginClazz(@PathVariable("pluginClazz") String pluginClazz) throws ThingsboardException {
checkParameter("pluginClazz", pluginClazz);
try {
return checkPluginActionsByPluginClazz(pluginClazz);
} catch (Exception e) {
throw handleException(e);
}
}
}

87
application/src/main/java/org/thingsboard/server/controller/CustomerController.java

@ -0,0 +1,87 @@
/**
* Copyright © 2016 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.controller;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.exception.ThingsboardException;
@RestController
@RequestMapping("/api")
public class CustomerController extends BaseController {
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/customer/{customerId}", method = RequestMethod.GET)
@ResponseBody
public Customer getCustomerById(@PathVariable("customerId") String strCustomerId) throws ThingsboardException {
checkParameter("customerId", strCustomerId);
try {
CustomerId customerId = new CustomerId(toUUID(strCustomerId));
return checkCustomerId(customerId);
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/customer", method = RequestMethod.POST)
@ResponseBody
public Customer saveCustomer(@RequestBody Customer customer) throws ThingsboardException {
try {
customer.setTenantId(getCurrentUser().getTenantId());
return checkNotNull(customerService.saveCustomer(customer));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/customer/{customerId}", method = RequestMethod.DELETE)
@ResponseStatus(value = HttpStatus.OK)
public void deleteCustomer(@PathVariable("customerId") String strCustomerId) throws ThingsboardException {
checkParameter("customerId", strCustomerId);
try {
CustomerId customerId = new CustomerId(toUUID(strCustomerId));
checkCustomerId(customerId);
customerService.deleteCustomer(customerId);
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/customers", params = { "limit" }, method = RequestMethod.GET)
@ResponseBody
public TextPageData<Customer> getCustomers(@RequestParam int limit,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String idOffset,
@RequestParam(required = false) String textOffset) throws ThingsboardException {
try {
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
TenantId tenantId = getCurrentUser().getTenantId();
return checkNotNull(customerService.findCustomersByTenantId(tenantId, pageLink));
} catch (Exception e) {
throw handleException(e);
}
}
}

148
application/src/main/java/org/thingsboard/server/controller/DashboardController.java

@ -0,0 +1,148 @@
/**
* Copyright © 2016 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.controller;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.exception.ThingsboardException;
@RestController
@RequestMapping("/api")
public class DashboardController extends BaseController {
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/dashboard/{dashboardId}", method = RequestMethod.GET)
@ResponseBody
public Dashboard getDashboardById(@PathVariable("dashboardId") String strDashboardId) throws ThingsboardException {
checkParameter("dashboardId", strDashboardId);
try {
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
return checkDashboardId(dashboardId);
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/dashboard", method = RequestMethod.POST)
@ResponseBody
public Dashboard saveDashboard(@RequestBody Dashboard dashboard) throws ThingsboardException {
try {
dashboard.setTenantId(getCurrentUser().getTenantId());
return checkNotNull(dashboardService.saveDashboard(dashboard));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/dashboard/{dashboardId}", method = RequestMethod.DELETE)
@ResponseStatus(value = HttpStatus.OK)
public void deleteDashboard(@PathVariable("dashboardId") String strDashboardId) throws ThingsboardException {
checkParameter("dashboardId", strDashboardId);
try {
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
checkDashboardId(dashboardId);
dashboardService.deleteDashboard(dashboardId);
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/customer/{customerId}/dashboard/{dashboardId}", method = RequestMethod.POST)
@ResponseBody
public Dashboard assignDashboardToCustomer(@PathVariable("customerId") String strCustomerId,
@PathVariable("dashboardId") String strDashboardId) throws ThingsboardException {
checkParameter("customerId", strCustomerId);
checkParameter("dashboardId", strDashboardId);
try {
CustomerId customerId = new CustomerId(toUUID(strCustomerId));
checkCustomerId(customerId);
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
checkDashboardId(dashboardId);
return checkNotNull(dashboardService.assignDashboardToCustomer(dashboardId, customerId));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/customer/dashboard/{dashboardId}", method = RequestMethod.DELETE)
@ResponseBody
public Dashboard unassignDashboardFromCustomer(@PathVariable("dashboardId") String strDashboardId) throws ThingsboardException {
checkParameter("dashboardId", strDashboardId);
try {
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId);
if (dashboard.getCustomerId() == null || dashboard.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) {
throw new IncorrectParameterException("Dashboard isn't assigned to any customer!");
}
return checkNotNull(dashboardService.unassignDashboardFromCustomer(dashboardId));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/tenant/dashboards", params = { "limit" }, method = RequestMethod.GET)
@ResponseBody
public TextPageData<Dashboard> getTenantDashboards(
@RequestParam int limit,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String idOffset,
@RequestParam(required = false) String textOffset) throws ThingsboardException {
try {
TenantId tenantId = getCurrentUser().getTenantId();
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
return checkNotNull(dashboardService.findDashboardsByTenantId(tenantId, pageLink));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/customer/{customerId}/dashboards", params = { "limit" }, method = RequestMethod.GET)
@ResponseBody
public TextPageData<Dashboard> getCustomerDashboards(
@PathVariable("customerId") String strCustomerId,
@RequestParam int limit,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String idOffset,
@RequestParam(required = false) String textOffset) throws ThingsboardException {
checkParameter("customerId", strCustomerId);
try {
TenantId tenantId = getCurrentUser().getTenantId();
CustomerId customerId = new CustomerId(toUUID(strCustomerId));
checkCustomerId(customerId);
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
return checkNotNull(dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink));
} catch (Exception e) {
throw handleException(e);
}
}
}

176
application/src/main/java/org/thingsboard/server/controller/DeviceController.java

@ -0,0 +1,176 @@
/**
* Copyright © 2016 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.controller;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.exception.ThingsboardException;
@RestController
@RequestMapping("/api")
public class DeviceController extends BaseController {
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/device/{deviceId}", method = RequestMethod.GET)
@ResponseBody
public Device getDeviceById(@PathVariable("deviceId") String strDeviceId) throws ThingsboardException {
checkParameter("deviceId", strDeviceId);
try {
DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
return checkDeviceId(deviceId);
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/device", method = RequestMethod.POST)
@ResponseBody
public Device saveDevice(@RequestBody Device device) throws ThingsboardException {
try {
device.setTenantId(getCurrentUser().getTenantId());
return checkNotNull(deviceService.saveDevice(device));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/device/{deviceId}", method = RequestMethod.DELETE)
@ResponseStatus(value = HttpStatus.OK)
public void deleteDevice(@PathVariable("deviceId") String strDeviceId) throws ThingsboardException {
checkParameter("deviceId", strDeviceId);
try {
DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
checkDeviceId(deviceId);
deviceService.deleteDevice(deviceId);
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/customer/{customerId}/device/{deviceId}", method = RequestMethod.POST)
@ResponseBody
public Device assignDeviceToCustomer(@PathVariable("customerId") String strCustomerId,
@PathVariable("deviceId") String strDeviceId) throws ThingsboardException {
checkParameter("customerId", strCustomerId);
checkParameter("deviceId", strDeviceId);
try {
CustomerId customerId = new CustomerId(toUUID(strCustomerId));
checkCustomerId(customerId);
DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
checkDeviceId(deviceId);
return checkNotNull(deviceService.assignDeviceToCustomer(deviceId, customerId));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/customer/device/{deviceId}", method = RequestMethod.DELETE)
@ResponseBody
public Device unassignDeviceFromCustomer(@PathVariable("deviceId") String strDeviceId) throws ThingsboardException {
checkParameter("deviceId", strDeviceId);
try {
DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
Device device = checkDeviceId(deviceId);
if (device.getCustomerId() == null || device.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) {
throw new IncorrectParameterException("Device isn't assigned to any customer!");
}
return checkNotNull(deviceService.unassignDeviceFromCustomer(deviceId));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/device/{deviceId}/credentials", method = RequestMethod.GET)
@ResponseBody
public DeviceCredentials getDeviceCredentialsByDeviceId(@PathVariable("deviceId") String strDeviceId) throws ThingsboardException {
checkParameter("deviceId", strDeviceId);
try {
DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
checkDeviceId(deviceId);
return checkNotNull(deviceCredentialsService.findDeviceCredentialsByDeviceId(deviceId));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/device/credentials", method = RequestMethod.POST)
@ResponseBody
public DeviceCredentials saveDeviceCredentials(@RequestBody DeviceCredentials deviceCredentials) throws ThingsboardException {
checkNotNull(deviceCredentials);
try {
checkDeviceId(deviceCredentials.getDeviceId());
return checkNotNull(deviceCredentialsService.updateDeviceCredentials(deviceCredentials));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/tenant/devices", params = { "limit" }, method = RequestMethod.GET)
@ResponseBody
public TextPageData<Device> getTenantDevices(
@RequestParam int limit,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String idOffset,
@RequestParam(required = false) String textOffset) throws ThingsboardException {
try {
TenantId tenantId = getCurrentUser().getTenantId();
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
return checkNotNull(deviceService.findDevicesByTenantId(tenantId, pageLink));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/customer/{customerId}/devices", params = { "limit" }, method = RequestMethod.GET)
@ResponseBody
public TextPageData<Device> getCustomerDevices(
@PathVariable("customerId") String strCustomerId,
@RequestParam int limit,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String idOffset,
@RequestParam(required = false) String textOffset) throws ThingsboardException {
checkParameter("customerId", strCustomerId);
try {
TenantId tenantId = getCurrentUser().getTenantId();
CustomerId customerId = new CustomerId(toUUID(strCustomerId));
checkCustomerId(customerId);
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
return checkNotNull(deviceService.findDevicesByTenantIdAndCustomerId(tenantId, customerId, pageLink));
} catch (Exception e) {
throw handleException(e);
}
}
}

116
application/src/main/java/org/thingsboard/server/controller/EventController.java

@ -0,0 +1,116 @@
/**
* Copyright © 2016 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.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.Event;
import org.thingsboard.server.common.data.id.*;
import org.thingsboard.server.common.data.page.TimePageData;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.dao.event.EventService;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.exception.ThingsboardErrorCode;
import org.thingsboard.server.exception.ThingsboardException;
@RestController
@RequestMapping("/api")
public class EventController extends BaseController {
@Autowired
private EventService eventService;
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/events/{entityType}/{entityId}/{eventType}", method = RequestMethod.GET)
@ResponseBody
public TimePageData<Event> getEvents(
@PathVariable("entityType") String strEntityType,
@PathVariable("entityId") String strEntityId,
@PathVariable("eventType") String eventType,
@RequestParam("tenantId") String strTenantId,
@RequestParam int limit,
@RequestParam(required = false) Long startTime,
@RequestParam(required = false) Long endTime,
@RequestParam(required = false, defaultValue = "false") boolean ascOrder,
@RequestParam(required = false) String offset
) throws ThingsboardException {
checkParameter("EntityId", strEntityId);
checkParameter("EntityType", strEntityType);
try {
TenantId tenantId = new TenantId(toUUID(strTenantId));
if (!tenantId.getId().equals(ModelConstants.NULL_UUID) &&
!tenantId.equals(getCurrentUser().getTenantId())) {
throw new ThingsboardException("You don't have permission to perform this operation!",
ThingsboardErrorCode.PERMISSION_DENIED);
}
TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset);
return checkNotNull(eventService.findEvents(tenantId, getEntityId(strEntityType, strEntityId), eventType, pageLink));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/events/{entityType}/{entityId}", method = RequestMethod.GET)
@ResponseBody
public TimePageData<Event> getEvents(
@PathVariable("entityType") String strEntityType,
@PathVariable("entityId") String strEntityId,
@RequestParam("tenantId") String strTenantId,
@RequestParam int limit,
@RequestParam(required = false) Long startTime,
@RequestParam(required = false) Long endTime,
@RequestParam(required = false, defaultValue = "false") boolean ascOrder,
@RequestParam(required = false) String offset
) throws ThingsboardException {
checkParameter("EntityId", strEntityId);
checkParameter("EntityType", strEntityType);
try {
TenantId tenantId = new TenantId(toUUID(strTenantId));
if (!tenantId.getId().equals(ModelConstants.NULL_UUID) &&
!tenantId.equals(getCurrentUser().getTenantId())) {
throw new ThingsboardException("You don't have permission to perform this operation!",
ThingsboardErrorCode.PERMISSION_DENIED);
}
TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset);
return checkNotNull(eventService.findEvents(tenantId, getEntityId(strEntityType, strEntityId), pageLink));
} catch (Exception e) {
throw handleException(e);
}
}
private EntityId getEntityId(String strEntityType, String strEntityId) throws ThingsboardException {
EntityId entityId;
EntityType entityType = EntityType.valueOf(strEntityType);
switch (entityType) {
case RULE:
entityId = new RuleId(toUUID(strEntityId));
break;
case PLUGIN:
entityId = new PluginId(toUUID(strEntityId));
break;
case DEVICE:
entityId = new DeviceId(toUUID(strEntityId));
break;
default:
throw new ThingsboardException("EntityType ['" + entityType + "'] is incorrect!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
}
return entityId;
}
}

197
application/src/main/java/org/thingsboard/server/controller/PluginController.java

@ -0,0 +1,197 @@
/**
* Copyright © 2016 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.controller;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.thingsboard.server.common.data.id.PluginId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.data.plugin.PluginMetaData;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.widget.WidgetsBundle;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.exception.ThingsboardException;
import java.util.List;
@RestController
@RequestMapping("/api")
public class PluginController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/plugin/{pluginId}", method = RequestMethod.GET)
@ResponseBody
public PluginMetaData getPluginById(@PathVariable("pluginId") String strPluginId) throws ThingsboardException {
checkParameter("pluginId", strPluginId);
try {
PluginId pluginId = new PluginId(toUUID(strPluginId));
return checkPlugin(pluginService.findPluginById(pluginId));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/plugin/token/{pluginToken}", method = RequestMethod.GET)
@ResponseBody
public PluginMetaData getPluginByToken(@PathVariable("pluginToken") String pluginToken) throws ThingsboardException {
checkParameter("pluginToken", pluginToken);
try {
return checkPlugin(pluginService.findPluginByApiToken(pluginToken));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/plugin", method = RequestMethod.POST)
@ResponseBody
public PluginMetaData savePlugin(@RequestBody PluginMetaData source) throws ThingsboardException {
try {
boolean created = source.getId() == null;
source.setTenantId(getCurrentUser().getTenantId());
PluginMetaData plugin = checkNotNull(pluginService.savePlugin(source));
actorService.onPluginStateChange(plugin.getTenantId(), plugin.getId(),
created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
return plugin;
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/plugin/{pluginId}/activate", method = RequestMethod.POST)
@ResponseStatus(value = HttpStatus.OK)
public void activatePluginById(@PathVariable("pluginId") String strPluginId) throws ThingsboardException {
checkParameter("pluginId", strPluginId);
try {
PluginId pluginId = new PluginId(toUUID(strPluginId));
PluginMetaData plugin = checkPlugin(pluginService.findPluginById(pluginId));
pluginService.activatePluginById(pluginId);
actorService.onPluginStateChange(plugin.getTenantId(), plugin.getId(), ComponentLifecycleEvent.ACTIVATED);
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/plugin/{pluginId}/suspend", method = RequestMethod.POST)
@ResponseStatus(value = HttpStatus.OK)
public void suspendPluginById(@PathVariable("pluginId") String strPluginId) throws ThingsboardException {
checkParameter("pluginId", strPluginId);
try {
PluginId pluginId = new PluginId(toUUID(strPluginId));
PluginMetaData plugin = checkPlugin(pluginService.findPluginById(pluginId));
pluginService.suspendPluginById(pluginId);
actorService.onPluginStateChange(plugin.getTenantId(), plugin.getId(), ComponentLifecycleEvent.SUSPENDED);
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('SYS_ADMIN')")
@RequestMapping(value = "/plugin/system", params = {"limit"}, method = RequestMethod.GET)
@ResponseBody
public TextPageData<PluginMetaData> getSystemPlugins(
@RequestParam int limit,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String idOffset,
@RequestParam(required = false) String textOffset) throws ThingsboardException {
try {
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
return checkNotNull(pluginService.findSystemPlugins(pageLink));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('SYS_ADMIN')")
@RequestMapping(value = "/plugin/tenant/{tenantId}", params = {"limit"}, method = RequestMethod.GET)
@ResponseBody
public TextPageData<PluginMetaData> getTenantPlugins(
@PathVariable("tenantId") String strTenantId,
@RequestParam int limit,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String idOffset,
@RequestParam(required = false) String textOffset) throws ThingsboardException {
checkParameter("tenantId", strTenantId);
try {
TenantId tenantId = new TenantId(toUUID(strTenantId));
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
return checkNotNull(pluginService.findTenantPlugins(tenantId, pageLink));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/plugins", method = RequestMethod.GET)
@ResponseBody
public List<PluginMetaData> getPlugins() throws ThingsboardException {
try {
if (getCurrentUser().getAuthority() == Authority.SYS_ADMIN) {
return checkNotNull(pluginService.findSystemPlugins());
} else {
TenantId tenantId = getCurrentUser().getTenantId();
List<PluginMetaData> plugins = checkNotNull(pluginService.findAllTenantPluginsByTenantId(tenantId));
plugins.stream()
.filter(plugin -> plugin.getTenantId().getId().equals(ModelConstants.NULL_UUID))
.forEach(plugin -> plugin.setConfiguration(null));
return plugins;
}
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/plugin", params = {"limit"}, method = RequestMethod.GET)
@ResponseBody
public TextPageData<PluginMetaData> getTenantPlugins(
@RequestParam int limit,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String idOffset,
@RequestParam(required = false) String textOffset) throws ThingsboardException {
try {
TenantId tenantId = getCurrentUser().getTenantId();
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
return checkNotNull(pluginService.findTenantPlugins(tenantId, pageLink));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/plugin/{pluginId}", method = RequestMethod.DELETE)
@ResponseStatus(value = HttpStatus.OK)
public void deletePlugin(@PathVariable("pluginId") String strPluginId) throws ThingsboardException {
checkParameter("pluginId", strPluginId);
try {
PluginId pluginId = new PluginId(toUUID(strPluginId));
PluginMetaData plugin = checkPlugin(pluginService.findPluginById(pluginId));
pluginService.deletePluginById(pluginId);
actorService.onPluginStateChange(plugin.getTenantId(), plugin.getId(), ComponentLifecycleEvent.DELETED);
} catch (Exception e) {
throw handleException(e);
}
}
}

193
application/src/main/java/org/thingsboard/server/controller/RuleController.java

@ -0,0 +1,193 @@
/**
* Copyright © 2016 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.controller;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.thingsboard.server.common.data.id.RuleId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.data.plugin.PluginMetaData;
import org.thingsboard.server.common.data.rule.RuleMetaData;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.exception.ThingsboardException;
import java.util.List;
@RestController
@RequestMapping("/api")
public class RuleController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/rule/{ruleId}", method = RequestMethod.GET)
@ResponseBody
public RuleMetaData getRuleById(@PathVariable("ruleId") String strRuleId) throws ThingsboardException {
checkParameter("ruleId", strRuleId);
try {
RuleId ruleId = new RuleId(toUUID(strRuleId));
return checkRule(ruleService.findRuleById(ruleId));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/rule/token/{pluginToken}", method = RequestMethod.GET)
@ResponseBody
public List<RuleMetaData> getRulesByPluginToken(@PathVariable("pluginToken") String pluginToken) throws ThingsboardException {
checkParameter("pluginToken", pluginToken);
try {
PluginMetaData plugin = checkPlugin(pluginService.findPluginByApiToken(pluginToken));
return ruleService.findPluginRules(plugin.getApiToken());
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/rule", method = RequestMethod.POST)
@ResponseBody
public RuleMetaData saveRule(@RequestBody RuleMetaData source) throws ThingsboardException {
try {
boolean created = source.getId() == null;
source.setTenantId(getCurrentUser().getTenantId());
RuleMetaData rule = checkNotNull(ruleService.saveRule(source));
actorService.onRuleStateChange(rule.getTenantId(), rule.getId(),
created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
return rule;
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/rule/{ruleId}/activate", method = RequestMethod.POST)
@ResponseStatus(value = HttpStatus.OK)
public void activateRuleById(@PathVariable("ruleId") String strRuleId) throws ThingsboardException {
checkParameter("ruleId", strRuleId);
try {
RuleId ruleId = new RuleId(toUUID(strRuleId));
RuleMetaData rule = checkRule(ruleService.findRuleById(ruleId));
ruleService.activateRuleById(ruleId);
actorService.onRuleStateChange(rule.getTenantId(), rule.getId(), ComponentLifecycleEvent.ACTIVATED);
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/rule/{ruleId}/suspend", method = RequestMethod.POST)
@ResponseStatus(value = HttpStatus.OK)
public void suspendRuleById(@PathVariable("ruleId") String strRuleId) throws ThingsboardException {
checkParameter("ruleId", strRuleId);
try {
RuleId ruleId = new RuleId(toUUID(strRuleId));
RuleMetaData rule = checkRule(ruleService.findRuleById(ruleId));
ruleService.suspendRuleById(ruleId);
actorService.onRuleStateChange(rule.getTenantId(), rule.getId(), ComponentLifecycleEvent.SUSPENDED);
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('SYS_ADMIN')")
@RequestMapping(value = "/rule/system", params = {"limit"}, method = RequestMethod.GET)
@ResponseBody
public TextPageData<RuleMetaData> getSystemRules(
@RequestParam int limit,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String idOffset,
@RequestParam(required = false) String textOffset) throws ThingsboardException {
try {
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
return checkNotNull(ruleService.findSystemRules(pageLink));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('SYS_ADMIN')")
@RequestMapping(value = "/rule/tenant/{tenantId}", params = {"limit"}, method = RequestMethod.GET)
@ResponseBody
public TextPageData<RuleMetaData> getTenantRules(
@PathVariable("tenantId") String strTenantId,
@RequestParam int limit,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String idOffset,
@RequestParam(required = false) String textOffset) throws ThingsboardException {
checkParameter("tenantId", strTenantId);
try {
TenantId tenantId = new TenantId(toUUID(strTenantId));
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
return checkNotNull(ruleService.findTenantRules(tenantId, pageLink));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/rules", method = RequestMethod.GET)
@ResponseBody
public List<RuleMetaData> getRules() throws ThingsboardException {
try {
if (getCurrentUser().getAuthority() == Authority.SYS_ADMIN) {
return checkNotNull(ruleService.findSystemRules());
} else {
TenantId tenantId = getCurrentUser().getTenantId();
return checkNotNull(ruleService.findAllTenantRulesByTenantId(tenantId));
}
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/rule", params = {"limit"}, method = RequestMethod.GET)
@ResponseBody
public TextPageData<RuleMetaData> getTenantRules(
@RequestParam int limit,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String idOffset,
@RequestParam(required = false) String textOffset) throws ThingsboardException {
try {
TenantId tenantId = getCurrentUser().getTenantId();
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
return checkNotNull(ruleService.findTenantRules(tenantId, pageLink));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/rule/{ruleId}", method = RequestMethod.DELETE)
@ResponseStatus(value = HttpStatus.OK)
public void deleteRule(@PathVariable("ruleId") String strRuleId) throws ThingsboardException {
checkParameter("ruleId", strRuleId);
try {
RuleId ruleId = new RuleId(toUUID(strRuleId));
RuleMetaData rule = checkRule(ruleService.findRuleById(ruleId));
ruleService.deleteRuleById(ruleId);
actorService.onRuleStateChange(rule.getTenantId(), rule.getId(), ComponentLifecycleEvent.DELETED);
} catch (Exception e) {
throw handleException(e);
}
}
}

89
application/src/main/java/org/thingsboard/server/controller/TenantController.java

@ -0,0 +1,89 @@
/**
* Copyright © 2016 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.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.exception.ThingsboardException;
@RestController
@RequestMapping("/api")
public class TenantController extends BaseController {
@Autowired
private TenantService tenantService;
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/tenant/{tenantId}", method = RequestMethod.GET)
@ResponseBody
public Tenant getTenantById(@PathVariable("tenantId") String strTenantId) throws ThingsboardException {
checkParameter("tenantId", strTenantId);
try {
TenantId tenantId = new TenantId(toUUID(strTenantId));
checkTenantId(tenantId);
return checkNotNull(tenantService.findTenantById(tenantId));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('SYS_ADMIN')")
@RequestMapping(value = "/tenant", method = RequestMethod.POST)
@ResponseBody
public Tenant saveTenant(@RequestBody Tenant tenant) throws ThingsboardException {
try {
return checkNotNull(tenantService.saveTenant(tenant));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('SYS_ADMIN')")
@RequestMapping(value = "/tenant/{tenantId}", method = RequestMethod.DELETE)
@ResponseStatus(value = HttpStatus.OK)
public void deleteTenant(@PathVariable("tenantId") String strTenantId) throws ThingsboardException {
checkParameter("tenantId", strTenantId);
try {
TenantId tenantId = new TenantId(toUUID(strTenantId));
tenantService.deleteTenant(tenantId);
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('SYS_ADMIN')")
@RequestMapping(value = "/tenants", params = { "limit" }, method = RequestMethod.GET)
@ResponseBody
public TextPageData<Tenant> getTenants(@RequestParam int limit,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String idOffset,
@RequestParam(required = false) String textOffset) throws ThingsboardException {
try {
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
return checkNotNull(tenantService.findTenants(pageLink));
} catch (Exception e) {
throw handleException(e);
}
}
}

179
application/src/main/java/org/thingsboard/server/controller/UserController.java

@ -0,0 +1,179 @@
/**
* Copyright © 2016 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.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.thingsboard.server.common.data.User;
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.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.security.UserCredentials;
import org.thingsboard.server.exception.ThingsboardErrorCode;
import org.thingsboard.server.exception.ThingsboardException;
import org.thingsboard.server.service.mail.MailService;
import org.thingsboard.server.service.security.model.SecurityUser;
import javax.servlet.http.HttpServletRequest;
@RestController
@RequestMapping("/api")
public class UserController extends BaseController {
@Autowired
private MailService mailService;
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/user/{userId}", method = RequestMethod.GET)
@ResponseBody
public User getUserById(@PathVariable("userId") String strUserId) throws ThingsboardException {
checkParameter("userId", strUserId);
try {
UserId userId = new UserId(toUUID(strUserId));
SecurityUser authUser = getCurrentUser();
if (authUser.getAuthority() == Authority.CUSTOMER_USER && !authUser.getId().equals(userId)) {
throw new ThingsboardException("You don't have permission to perform this operation!",
ThingsboardErrorCode.PERMISSION_DENIED);
}
return checkUserId(userId);
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/user", method = RequestMethod.POST)
@ResponseBody
public User saveUser(@RequestBody User user,
HttpServletRequest request) throws ThingsboardException {
try {
SecurityUser authUser = getCurrentUser();
if (authUser.getAuthority() == Authority.CUSTOMER_USER && !authUser.getId().equals(user.getId())) {
throw new ThingsboardException("You don't have permission to perform this operation!",
ThingsboardErrorCode.PERMISSION_DENIED);
}
boolean sendEmail = user.getId() == null;
if (getCurrentUser().getAuthority() == Authority.TENANT_ADMIN) {
user.setTenantId(getCurrentUser().getTenantId());
}
User savedUser = checkNotNull(userService.saveUser(user));
if (sendEmail) {
UserCredentials userCredentials = userService.findUserCredentialsByUserId(savedUser.getId());
String baseUrl = String.format("%s://%s:%d",
request.getScheme(),
request.getServerName(),
request.getServerPort());
String activateUrl = String.format("%s/api/noauth/activate?activateToken=%s", baseUrl,
userCredentials.getActivateToken());
String email = savedUser.getEmail();
try {
mailService.sendActivationEmail(activateUrl, email);
} catch (ThingsboardException e) {
userService.deleteUser(savedUser.getId());
throw e;
}
}
return savedUser;
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/user/sendActivationMail", method = RequestMethod.POST)
@ResponseStatus(value = HttpStatus.OK)
public void sendActivationEmail(
@RequestParam(value = "email") String email,
HttpServletRequest request) throws ThingsboardException {
try {
User user = checkNotNull(userService.findUserByEmail(email));
UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getId());
if (!userCredentials.isEnabled()) {
String baseUrl = String.format("%s://%s:%d",
request.getScheme(),
request.getServerName(),
request.getServerPort());
String activateUrl = String.format("%s/api/noauth/activate?activateToken=%s", baseUrl,
userCredentials.getActivateToken());
mailService.sendActivationEmail(activateUrl, email);
} else {
throw new ThingsboardException("User is already active!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
}
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/user/{userId}", method = RequestMethod.DELETE)
@ResponseStatus(value = HttpStatus.OK)
public void deleteUser(@PathVariable("userId") String strUserId) throws ThingsboardException {
checkParameter("userId", strUserId);
try {
UserId userId = new UserId(toUUID(strUserId));
checkUserId(userId);
userService.deleteUser(userId);
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('SYS_ADMIN')")
@RequestMapping(value = "/tenant/{tenantId}/users", params = { "limit" }, method = RequestMethod.GET)
@ResponseBody
public TextPageData<User> getTenantAdmins(
@PathVariable("tenantId") String strTenantId,
@RequestParam int limit,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String idOffset,
@RequestParam(required = false) String textOffset) throws ThingsboardException {
checkParameter("tenantId", strTenantId);
try {
TenantId tenantId = new TenantId(toUUID(strTenantId));
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
return checkNotNull(userService.findTenantAdmins(tenantId, pageLink));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/customer/{customerId}/users", params = { "limit" }, method = RequestMethod.GET)
@ResponseBody
public TextPageData<User> getCustomerUsers(
@PathVariable("customerId") String strCustomerId,
@RequestParam int limit,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String idOffset,
@RequestParam(required = false) String textOffset) throws ThingsboardException {
checkParameter("customerId", strCustomerId);
try {
CustomerId customerId = new CustomerId(toUUID(strCustomerId));
checkCustomerId(customerId);
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
TenantId tenantId = getCurrentUser().getTenantId();
return checkNotNull(userService.findCustomerUsers(tenantId, customerId, pageLink));
} catch (Exception e) {
throw handleException(e);
}
}
}

118
application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java

@ -0,0 +1,118 @@
/**
* Copyright © 2016 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.controller;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.WidgetTypeId;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.widget.WidgetType;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.exception.ThingsboardException;
import java.util.List;
@RestController
@RequestMapping("/api")
public class WidgetTypeController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/widgetType/{widgetTypeId}", method = RequestMethod.GET)
@ResponseBody
public WidgetType getWidgetTypeById(@PathVariable("widgetTypeId") String strWidgetTypeId) throws ThingsboardException {
checkParameter("widgetTypeId", strWidgetTypeId);
try {
WidgetTypeId widgetTypeId = new WidgetTypeId(toUUID(strWidgetTypeId));
return checkWidgetTypeId(widgetTypeId, false);
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/widgetType", method = RequestMethod.POST)
@ResponseBody
public WidgetType saveWidgetType(@RequestBody WidgetType widgetType) throws ThingsboardException {
try {
if (getCurrentUser().getAuthority() == Authority.SYS_ADMIN) {
widgetType.setTenantId(new TenantId(ModelConstants.NULL_UUID));
} else {
widgetType.setTenantId(getCurrentUser().getTenantId());
}
return checkNotNull(widgetTypeService.saveWidgetType(widgetType));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/widgetType/{widgetTypeId}", method = RequestMethod.DELETE)
@ResponseStatus(value = HttpStatus.OK)
public void deleteWidgetType(@PathVariable("widgetTypeId") String strWidgetTypeId) throws ThingsboardException {
checkParameter("widgetTypeId", strWidgetTypeId);
try {
WidgetTypeId widgetTypeId = new WidgetTypeId(toUUID(strWidgetTypeId));
checkWidgetTypeId(widgetTypeId, true);
widgetTypeService.deleteWidgetType(widgetTypeId);
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/widgetTypes", params = { "isSystem", "bundleAlias"}, method = RequestMethod.GET)
@ResponseBody
public List<WidgetType> getBundleWidgetTypes(
@RequestParam boolean isSystem,
@RequestParam String bundleAlias) throws ThingsboardException {
try {
TenantId tenantId;
if (isSystem) {
tenantId = new TenantId(ModelConstants.NULL_UUID);
} else {
tenantId = getCurrentUser().getTenantId();
}
return checkNotNull(widgetTypeService.findWidgetTypesByTenantIdAndBundleAlias(tenantId, bundleAlias));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/widgetType", params = { "isSystem", "bundleAlias", "alias" }, method = RequestMethod.GET)
@ResponseBody
public WidgetType getWidgetType(
@RequestParam boolean isSystem,
@RequestParam String bundleAlias,
@RequestParam String alias) throws ThingsboardException {
try {
TenantId tenantId;
if (isSystem) {
tenantId = new TenantId(ModelConstants.NULL_UUID);
} else {
tenantId = getCurrentUser().getTenantId();
}
WidgetType widgetType = widgetTypeService.findWidgetTypeByTenantIdBundleAliasAndAlias(tenantId, bundleAlias, alias);
checkWidgetType(widgetType, false);
return widgetType;
} catch (Exception e) {
throw handleException(e);
}
}
}

116
application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java

@ -0,0 +1,116 @@
/**
* Copyright © 2016 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.controller;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.WidgetsBundleId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.widget.WidgetsBundle;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.exception.ThingsboardException;
import java.util.List;
@RestController
@RequestMapping("/api")
public class WidgetsBundleController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/widgetsBundle/{widgetsBundleId}", method = RequestMethod.GET)
@ResponseBody
public WidgetsBundle getWidgetsBundleById(@PathVariable("widgetsBundleId") String strWidgetsBundleId) throws ThingsboardException {
checkParameter("widgetsBundleId", strWidgetsBundleId);
try {
WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId));
return checkWidgetsBundleId(widgetsBundleId, false);
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/widgetsBundle", method = RequestMethod.POST)
@ResponseBody
public WidgetsBundle saveWidgetsBundle(@RequestBody WidgetsBundle widgetsBundle) throws ThingsboardException {
try {
if (getCurrentUser().getAuthority() == Authority.SYS_ADMIN) {
widgetsBundle.setTenantId(new TenantId(ModelConstants.NULL_UUID));
} else {
widgetsBundle.setTenantId(getCurrentUser().getTenantId());
}
return checkNotNull(widgetsBundleService.saveWidgetsBundle(widgetsBundle));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/widgetsBundle/{widgetsBundleId}", method = RequestMethod.DELETE)
@ResponseStatus(value = HttpStatus.OK)
public void deleteWidgetsBundle(@PathVariable("widgetsBundleId") String strWidgetsBundleId) throws ThingsboardException {
checkParameter("widgetsBundleId", strWidgetsBundleId);
try {
WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId));
checkWidgetsBundleId(widgetsBundleId, true);
widgetsBundleService.deleteWidgetsBundle(widgetsBundleId);
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/widgetsBundles", params = { "limit" }, method = RequestMethod.GET)
@ResponseBody
public TextPageData<WidgetsBundle> getWidgetsBundles(
@RequestParam int limit,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String idOffset,
@RequestParam(required = false) String textOffset) throws ThingsboardException {
try {
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
if (getCurrentUser().getAuthority() == Authority.SYS_ADMIN) {
return checkNotNull(widgetsBundleService.findSystemWidgetsBundlesByPageLink(pageLink));
} else {
TenantId tenantId = getCurrentUser().getTenantId();
return checkNotNull(widgetsBundleService.findAllTenantWidgetsBundlesByTenantIdAndPageLink(tenantId, pageLink));
}
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/widgetsBundles", method = RequestMethod.GET)
@ResponseBody
public List<WidgetsBundle> getWidgetsBundles() throws ThingsboardException {
try {
if (getCurrentUser().getAuthority() == Authority.SYS_ADMIN) {
return checkNotNull(widgetsBundleService.findSystemWidgetsBundles());
} else {
TenantId tenantId = getCurrentUser().getTenantId();
return checkNotNull(widgetsBundleService.findAllTenantWidgetsBundlesByTenantId(tenantId));
}
} catch (Exception e) {
throw handleException(e);
}
}
}

108
application/src/main/java/org/thingsboard/server/controller/plugin/PluginApiController.java

@ -0,0 +1,108 @@
/**
* Copyright © 2016 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.controller.plugin;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;
import org.thingsboard.server.actors.service.ActorService;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.plugin.PluginMetaData;
import org.thingsboard.server.controller.BaseController;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.plugin.PluginService;
import org.thingsboard.server.exception.ThingsboardException;
import org.thingsboard.server.extensions.api.plugins.PluginApiCallSecurityContext;
import org.thingsboard.server.extensions.api.plugins.PluginConstants;
import org.thingsboard.server.extensions.api.plugins.rest.BasicPluginRestMsg;
import org.thingsboard.server.extensions.api.plugins.rest.RestRequest;
import javax.servlet.http.HttpServletRequest;
@RestController
@RequestMapping(PluginConstants.PLUGIN_URL_PREFIX)
@Slf4j
public class PluginApiController extends BaseController {
@Autowired
private ActorService actorService;
@Autowired
private PluginService pluginService;
@SuppressWarnings("rawtypes")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/{pluginToken}/**")
@ResponseStatus(value = HttpStatus.OK)
public DeferredResult<ResponseEntity> processRequest(
@PathVariable("pluginToken") String pluginToken,
RequestEntity<byte[]> requestEntity,
HttpServletRequest request)
throws ThingsboardException {
log.debug("[{}] Going to process requst uri: {}", pluginToken, requestEntity.getUrl());
DeferredResult<ResponseEntity> result = new DeferredResult<ResponseEntity>();
PluginMetaData pluginMd = pluginService.findPluginByApiToken(pluginToken);
if (pluginMd == null) {
result.setErrorResult(new PluginNotFoundException("Plugin with token: " + pluginToken + " not found!"));
} else {
TenantId tenantId = getCurrentUser().getTenantId();
CustomerId customerId = getCurrentUser().getCustomerId();
if (validatePluginAccess(pluginMd, tenantId, customerId)) {
if(ModelConstants.NULL_UUID.equals(tenantId.getId())){
tenantId = null;
}
PluginApiCallSecurityContext securityCtx = new PluginApiCallSecurityContext(pluginMd.getTenantId(), pluginMd.getId(), tenantId, customerId);
actorService.process(new BasicPluginRestMsg(securityCtx, new RestRequest(requestEntity, request), result));
} else {
result.setResult(new ResponseEntity<>(HttpStatus.FORBIDDEN));
}
}
return result;
}
public static boolean validatePluginAccess(PluginMetaData pluginMd, TenantId tenantId, CustomerId customerId) {
boolean systemAdministrator = tenantId == null || ModelConstants.NULL_UUID.equals(tenantId.getId());
boolean tenantAdministrator = !systemAdministrator && (customerId == null || ModelConstants.NULL_UUID.equals(customerId.getId()));
boolean systemPlugin = ModelConstants.NULL_UUID.equals(pluginMd.getTenantId().getId());
boolean validUser = false;
if (systemPlugin) {
if (pluginMd.isPublicAccess() || systemAdministrator) {
// All users can access public system plugins. Only system
// users can access private system plugins
validUser = true;
}
} else {
if ((pluginMd.isPublicAccess() || tenantAdministrator) && tenantId.equals(pluginMd.getTenantId())) {
// All tenant users can access public tenant plugins. Only tenant
// administrator can access private tenant plugins
validUser = true;
}
}
return validUser;
}
}

30
application/src/main/java/org/thingsboard/server/controller/plugin/PluginNotFoundException.java

@ -0,0 +1,30 @@
/**
* Copyright © 2016 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.controller.plugin;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(HttpStatus.NOT_FOUND)
public class PluginNotFoundException extends RuntimeException {
private static final long serialVersionUID = 1L;
public PluginNotFoundException(String message){
super(message);
}
}

203
application/src/main/java/org/thingsboard/server/controller/plugin/PluginWebSocketHandler.java

@ -0,0 +1,203 @@
/**
* Copyright © 2016 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.controller.plugin;
import java.io.IOException;
import java.net.URI;
import java.security.InvalidParameterException;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.actors.service.ActorService;
import org.thingsboard.server.config.WebSocketConfiguration;
import org.thingsboard.server.extensions.api.plugins.PluginConstants;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.plugin.PluginMetaData;
import org.thingsboard.server.dao.plugin.PluginService;
import org.thingsboard.server.extensions.api.plugins.PluginApiCallSecurityContext;
import org.thingsboard.server.extensions.api.plugins.ws.BasicPluginWebsocketSessionRef;
import org.thingsboard.server.extensions.api.plugins.ws.PluginWebsocketSessionRef;
import org.thingsboard.server.extensions.api.plugins.ws.SessionEvent;
import org.thingsboard.server.extensions.api.plugins.ws.msg.PluginWebsocketMsg;
import org.thingsboard.server.extensions.api.plugins.ws.msg.SessionEventPluginWebSocketMsg;
import org.thingsboard.server.extensions.api.plugins.ws.msg.TextPluginWebSocketMsg;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
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;
@Service
@Slf4j
public class PluginWebSocketHandler extends TextWebSocketHandler implements PluginWebSocketMsgEndpoint {
private static final ConcurrentMap<String, SessionMetaData> internalSessionMap = new ConcurrentHashMap<>();
private static final ConcurrentMap<String, String> externalSessionMap = new ConcurrentHashMap<>();
@Autowired @Lazy
private ActorService actorService;
@Autowired @Lazy
private PluginService pluginService;
@Override
public void handleTextMessage(WebSocketSession session, TextMessage message) {
try {
log.info("[{}] Processing {}", session.getId(), message);
SessionMetaData sessionMd = internalSessionMap.get(session.getId());
if (sessionMd != null) {
actorService.process(new TextPluginWebSocketMsg(sessionMd.sessionRef, message.getPayload()));
} else {
log.warn("[{}] Failed to find session", session.getId());
session.close(CloseStatus.SERVER_ERROR.withReason("Session not found!"));
}
session.sendMessage(message);
} catch (IOException e) {
log.warn("IO error", e);
}
}
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
super.afterConnectionEstablished(session);
try {
String internalSessionId = session.getId();
PluginWebsocketSessionRef sessionRef = toRef(session);
String externalSessionId = sessionRef.getSessionId();
internalSessionMap.put(internalSessionId, new SessionMetaData(session, sessionRef));
externalSessionMap.put(externalSessionId, internalSessionId);
actorService.process(new SessionEventPluginWebSocketMsg(sessionRef, SessionEvent.onEstablished()));
log.info("[{}][{}] Session is started", externalSessionId, session.getId());
} catch (InvalidParameterException e) {
log.warn("[[{}] Failed to start session", session.getId(), e);
session.close(CloseStatus.BAD_DATA.withReason(e.getMessage()));
} catch (Exception e) {
log.warn("[{}] Failed to start session", session.getId(), e);
session.close(CloseStatus.SERVER_ERROR.withReason(e.getMessage()));
}
}
@Override
public void handleTransportError(WebSocketSession session, Throwable tError) throws Exception {
super.handleTransportError(session, tError);
SessionMetaData sessionMd = internalSessionMap.get(session.getId());
if (sessionMd != null) {
actorService.process(new SessionEventPluginWebSocketMsg(sessionMd.sessionRef, SessionEvent.onError(tError)));
} else {
log.warn("[{}] Failed to find session", session.getId());
}
log.trace("[{}] Session transport error", session.getId(), tError);
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
super.afterConnectionClosed(session, closeStatus);
SessionMetaData sessionMd = internalSessionMap.remove(session.getId());
if (sessionMd != null) {
externalSessionMap.remove(sessionMd.sessionRef.getSessionId());
actorService.process(new SessionEventPluginWebSocketMsg(sessionMd.sessionRef, SessionEvent.onClosed()));
}
log.info("[{}] Session is closed", session.getId());
}
private PluginWebsocketSessionRef toRef(WebSocketSession session) throws IOException {
URI sessionUri = session.getUri();
String path = sessionUri.getPath();
path = path.substring(WebSocketConfiguration.WS_PLUGIN_PREFIX.length());
if (path.length() == 0) {
throw new IllegalArgumentException("URL should contain plugin token!");
}
String[] pathElements = path.split("/");
String pluginToken = pathElements[0];
// TODO: cache
PluginMetaData pluginMd = pluginService.findPluginByApiToken(pluginToken);
if (pluginMd == null) {
throw new InvalidParameterException("Can't find plugin with specified token!");
} else {
SecurityUser currentUser = (SecurityUser) session.getAttributes().get(WebSocketConfiguration.WS_SECURITY_USER_ATTRIBUTE);
TenantId tenantId = currentUser.getTenantId();
CustomerId customerId = currentUser.getCustomerId();
if (PluginApiController.validatePluginAccess(pluginMd, tenantId, customerId)) {
PluginApiCallSecurityContext securityCtx = new PluginApiCallSecurityContext(pluginMd.getTenantId(), pluginMd.getId(), tenantId,
currentUser.getCustomerId());
return new BasicPluginWebsocketSessionRef(UUID.randomUUID().toString(), securityCtx, session.getUri(), session.getAttributes(),
session.getLocalAddress(), session.getRemoteAddress());
} else {
throw new SecurityException("Current user is not allowed to use this plugin!");
}
}
}
private static class SessionMetaData {
private final WebSocketSession session;
private final PluginWebsocketSessionRef sessionRef;
public SessionMetaData(WebSocketSession session, PluginWebsocketSessionRef sessionRef) {
super();
this.session = session;
this.sessionRef = sessionRef;
}
}
@Override
public void send(PluginWebsocketMsg<?> wsMsg) throws IOException {
PluginWebsocketSessionRef sessionRef = wsMsg.getSessionRef();
String externalId = sessionRef.getSessionId();
log.debug("[{}] Processing {}", externalId, wsMsg);
String internalId = externalSessionMap.get(externalId);
if (internalId != null) {
SessionMetaData sessionMd = internalSessionMap.get(internalId);
if (sessionMd != null) {
if (wsMsg instanceof TextPluginWebSocketMsg) {
String payload = ((TextPluginWebSocketMsg) wsMsg).getPayload();
sessionMd.session.sendMessage(new TextMessage(payload));
}
} else {
log.warn("[{}][{}] Failed to find session by internal id", externalId, internalId);
}
} else {
log.warn("[{}] Failed to find session by external id", externalId);
}
}
@Override
public void close(PluginWebsocketSessionRef sessionRef) throws IOException {
String externalId = sessionRef.getSessionId();
log.debug("[{}] Processing close request", externalId);
String internalId = externalSessionMap.get(externalId);
if (internalId != null) {
SessionMetaData sessionMd = internalSessionMap.get(internalId);
if (sessionMd != null) {
sessionMd.session.close(CloseStatus.NORMAL);
} else {
log.warn("[{}][{}] Failed to find session by internal id", externalId, internalId);
}
} else {
log.warn("[{}] Failed to find session by external id", externalId);
}
}
}

28
application/src/main/java/org/thingsboard/server/controller/plugin/PluginWebSocketMsgEndpoint.java

@ -0,0 +1,28 @@
/**
* Copyright © 2016 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.controller.plugin;
import java.io.IOException;
import org.thingsboard.server.extensions.api.plugins.ws.PluginWebsocketSessionRef;
import org.thingsboard.server.extensions.api.plugins.ws.msg.PluginWebsocketMsg;
public interface PluginWebSocketMsgEndpoint {
void send(PluginWebsocketMsg<?> wsMsg) throws IOException;
void close(PluginWebsocketSessionRef sessionRef) throws IOException;
}

41
application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorCode.java

@ -0,0 +1,41 @@
/**
* Copyright © 2016 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.exception;
import com.fasterxml.jackson.annotation.JsonValue;
public enum ThingsboardErrorCode {
GENERAL(2),
AUTHENTICATION(10),
JWT_TOKEN_EXPIRED(11),
PERMISSION_DENIED(20),
INVALID_ARGUMENTS(30),
BAD_REQUEST_PARAMS(31),
ITEM_NOT_FOUND(32);
private int errorCode;
ThingsboardErrorCode(int errorCode) {
this.errorCode = errorCode;
}
@JsonValue
public int getErrorCode() {
return errorCode;
}
}

60
application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponse.java

@ -0,0 +1,60 @@
/**
* Copyright © 2016 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.exception;
import org.springframework.http.HttpStatus;
import java.util.Date;
public class ThingsboardErrorResponse {
// HTTP Response Status Code
private final HttpStatus status;
// General Error message
private final String message;
// Error code
private final ThingsboardErrorCode errorCode;
private final Date timestamp;
protected ThingsboardErrorResponse(final String message, final ThingsboardErrorCode errorCode, HttpStatus status) {
this.message = message;
this.errorCode = errorCode;
this.status = status;
this.timestamp = new java.util.Date();
}
public static ThingsboardErrorResponse of(final String message, final ThingsboardErrorCode errorCode, HttpStatus status) {
return new ThingsboardErrorResponse(message, errorCode, status);
}
public Integer getStatus() {
return status.value();
}
public String getMessage() {
return message;
}
public ThingsboardErrorCode getErrorCode() {
return errorCode;
}
public Date getTimestamp() {
return timestamp;
}
}

133
application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java

@ -0,0 +1,133 @@
/**
* Copyright © 2016 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.exception;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import org.thingsboard.server.service.security.exception.AuthMethodNotSupportedException;
import org.thingsboard.server.service.security.exception.JwtExpiredTokenException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
@Slf4j
public class ThingsboardErrorResponseHandler implements AccessDeniedHandler {
@Autowired
private ObjectMapper mapper;
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException,
ServletException {
if (!response.isCommitted()) {
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setStatus(HttpStatus.FORBIDDEN.value());
mapper.writeValue(response.getWriter(),
ThingsboardErrorResponse.of("You don't have permission to perform this operation!",
ThingsboardErrorCode.PERMISSION_DENIED, HttpStatus.FORBIDDEN));
}
}
public void handle(Exception exception, HttpServletResponse response) {
log.debug("Processing exception {}", exception.getMessage(), exception);
if (!response.isCommitted()) {
try {
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
if (exception instanceof ThingsboardException) {
handleThingsboardException((ThingsboardException) exception, response);
} else if (exception instanceof AccessDeniedException) {
handleAccessDeniedException(response);
} else if (exception instanceof AuthenticationException) {
handleAuthenticationException((AuthenticationException) exception, response);
} else {
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
mapper.writeValue(response.getWriter(), ThingsboardErrorResponse.of(exception.getMessage(),
ThingsboardErrorCode.GENERAL, HttpStatus.INTERNAL_SERVER_ERROR));
}
} catch (IOException e) {
log.error("Can't handle exception", e);
}
}
}
private void handleThingsboardException(ThingsboardException thingsboardException, HttpServletResponse response) throws IOException {
ThingsboardErrorCode errorCode = thingsboardException.getErrorCode();
HttpStatus status;
switch (errorCode) {
case AUTHENTICATION:
status = HttpStatus.UNAUTHORIZED;
break;
case PERMISSION_DENIED:
status = HttpStatus.FORBIDDEN;
break;
case INVALID_ARGUMENTS:
status = HttpStatus.BAD_REQUEST;
break;
case ITEM_NOT_FOUND:
status = HttpStatus.NOT_FOUND;
break;
case BAD_REQUEST_PARAMS:
status = HttpStatus.BAD_REQUEST;
break;
case GENERAL:
status = HttpStatus.INTERNAL_SERVER_ERROR;
break;
default:
status = HttpStatus.INTERNAL_SERVER_ERROR;
break;
}
response.setStatus(status.value());
mapper.writeValue(response.getWriter(), ThingsboardErrorResponse.of(thingsboardException.getMessage(), errorCode, status));
}
private void handleAccessDeniedException(HttpServletResponse response) throws IOException {
response.setStatus(HttpStatus.FORBIDDEN.value());
mapper.writeValue(response.getWriter(),
ThingsboardErrorResponse.of("You don't have permission to perform this operation!",
ThingsboardErrorCode.PERMISSION_DENIED, HttpStatus.FORBIDDEN));
}
private void handleAuthenticationException(AuthenticationException authenticationException, HttpServletResponse response) throws IOException {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
if (authenticationException instanceof BadCredentialsException) {
mapper.writeValue(response.getWriter(), ThingsboardErrorResponse.of("Invalid username or password", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED));
} else if (authenticationException instanceof JwtExpiredTokenException) {
mapper.writeValue(response.getWriter(), ThingsboardErrorResponse.of("Token has expired", ThingsboardErrorCode.JWT_TOKEN_EXPIRED, HttpStatus.UNAUTHORIZED));
} else if (authenticationException instanceof AuthMethodNotSupportedException) {
mapper.writeValue(response.getWriter(), ThingsboardErrorResponse.of(authenticationException.getMessage(), ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED));
}
mapper.writeValue(response.getWriter(), ThingsboardErrorResponse.of("Authentication failed", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED));
}
}

51
application/src/main/java/org/thingsboard/server/exception/ThingsboardException.java

@ -0,0 +1,51 @@
/**
* Copyright © 2016 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.exception;
public class ThingsboardException extends Exception {
private static final long serialVersionUID = 1L;
private ThingsboardErrorCode errorCode;
public ThingsboardException() {
super();
}
public ThingsboardException(ThingsboardErrorCode errorCode) {
this.errorCode = errorCode;
}
public ThingsboardException(String message, ThingsboardErrorCode errorCode) {
super(message);
this.errorCode = errorCode;
}
public ThingsboardException(String message, Throwable cause, ThingsboardErrorCode errorCode) {
super(message, cause);
this.errorCode = errorCode;
}
public ThingsboardException(Throwable cause, ThingsboardErrorCode errorCode) {
super(cause);
this.errorCode = errorCode;
}
public ThingsboardErrorCode getErrorCode() {
return errorCode;
}
}

39
application/src/main/java/org/thingsboard/server/service/environment/EnvironmentLogService.java

@ -0,0 +1,39 @@
/**
* Copyright © 2016 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.environment;
import lombok.extern.slf4j.Slf4j;
import org.apache.zookeeper.Environment;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
/**
* Created by igor on 11/24/16.
*/
@Service("environmentLogService")
@ConditionalOnProperty(prefix = "zk", value = "enabled", havingValue = "false", matchIfMissing = true)
@Slf4j
public class EnvironmentLogService {
@PostConstruct
public void init() {
Environment.logEnv("Thingsboard server environment: ", log);
}
}

208
application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java

@ -0,0 +1,208 @@
/**
* Copyright © 2016 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.mail;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import javax.annotation.PostConstruct;
import javax.mail.internet.MimeMessage;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.velocity.app.VelocityEngine;
import org.thingsboard.server.exception.ThingsboardErrorCode;
import org.thingsboard.server.exception.ThingsboardException;
import org.thingsboard.server.common.data.AdminSettings;
import org.thingsboard.server.dao.settings.AdminSettingsService;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
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 com.fasterxml.jackson.databind.JsonNode;
@Service
@Slf4j
public class DefaultMailService implements MailService {
@Autowired
private MessageSource messages;
@Autowired
private VelocityEngine engine;
private JavaMailSenderImpl mailSender;
private String mailFrom;
@Autowired
private AdminSettingsService adminSettingsService;
@PostConstruct
private void init() {
updateMailConfiguration();
}
@Override
public void updateMailConfiguration() {
AdminSettings settings = adminSettingsService.findAdminSettingsByKey("mail");
JsonNode jsonConfig = settings.getJsonValue();
mailSender = createMailSender(jsonConfig);
mailFrom = jsonConfig.get("mailFrom").asText();
}
private JavaMailSenderImpl createMailSender(JsonNode jsonConfig) {
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
mailSender.setHost(jsonConfig.get("smtpHost").asText());
mailSender.setPort(parsePort(jsonConfig.get("smtpPort").asText()));
mailSender.setUsername(jsonConfig.get("username").asText());
mailSender.setPassword(jsonConfig.get("password").asText());
mailSender.setJavaMailProperties(createJavaMailProperties(jsonConfig));
return mailSender;
}
private Properties createJavaMailProperties(JsonNode jsonConfig) {
Properties javaMailProperties = new Properties();
String protocol = jsonConfig.get("smtpProtocol").asText();
javaMailProperties.put("mail.transport.protocol", protocol);
javaMailProperties.put("mail." + protocol + ".host", jsonConfig.get("smtpHost").asText());
javaMailProperties.put("mail." + protocol + ".port", jsonConfig.get("smtpPort").asText());
javaMailProperties.put("mail." + protocol + ".timeout", jsonConfig.get("timeout").asText());
javaMailProperties.put("mail." + protocol + ".auth", String.valueOf(StringUtils.isNotEmpty(jsonConfig.get("username").asText())));
javaMailProperties.put("mail." + protocol + ".starttls.enable", jsonConfig.get("enableTls"));
return javaMailProperties;
}
private int parsePort(String strPort) {
try {
return Integer.valueOf(strPort);
} catch (NumberFormatException e) {
throw new IncorrectParameterException(String.format("Invalid smtp port value: %s", strPort));
}
}
@Override
public void sendTestMail(JsonNode jsonConfig, String email) throws ThingsboardException {
JavaMailSenderImpl testMailSender = createMailSender(jsonConfig);
String mailFrom = jsonConfig.get("mailFrom").asText();
String subject = messages.getMessage("test.message.subject", null, Locale.US);
Map<String, Object> model = new HashMap<String, Object>();
model.put("targetEmail", email);
String message = VelocityEngineUtils.mergeTemplateIntoString(this.engine,
"test.vm", "UTF-8", model);
sendMail(testMailSender, mailFrom, email, subject, message);
}
@Override
public void sendActivationEmail(String activationLink, String email) throws ThingsboardException {
String subject = messages.getMessage("activation.subject", null, Locale.US);
Map<String, Object> model = new HashMap<String, Object>();
model.put("activationLink", activationLink);
model.put("targetEmail", email);
String message = VelocityEngineUtils.mergeTemplateIntoString(this.engine,
"activation.vm", "UTF-8", model);
sendMail(mailSender, mailFrom, email, subject, message);
}
@Override
public void sendAccountActivatedEmail(String loginLink, String email) throws ThingsboardException {
String subject = messages.getMessage("account.activated.subject", null, Locale.US);
Map<String, Object> model = new HashMap<String, Object>();
model.put("loginLink", loginLink);
model.put("targetEmail", email);
String message = VelocityEngineUtils.mergeTemplateIntoString(this.engine,
"account.activated.vm", "UTF-8", model);
sendMail(mailSender, mailFrom, email, subject, message);
}
@Override
public void sendResetPasswordEmail(String passwordResetLink, String email) throws ThingsboardException {
String subject = messages.getMessage("reset.password.subject", null, Locale.US);
Map<String, Object> model = new HashMap<String, Object>();
model.put("passwordResetLink", passwordResetLink);
model.put("targetEmail", email);
String message = VelocityEngineUtils.mergeTemplateIntoString(this.engine,
"reset.password.vm", "UTF-8", model);
sendMail(mailSender, mailFrom, email, subject, message);
}
@Override
public void sendPasswordWasResetEmail(String loginLink, String email) throws ThingsboardException {
String subject = messages.getMessage("password.was.reset.subject", null, Locale.US);
Map<String, Object> model = new HashMap<String, Object>();
model.put("loginLink", loginLink);
model.put("targetEmail", email);
String message = VelocityEngineUtils.mergeTemplateIntoString(this.engine,
"password.was.reset.vm", "UTF-8", model);
sendMail(mailSender, mailFrom, email, subject, message);
}
private void sendMail(JavaMailSenderImpl mailSender,
String mailFrom, String email,
String subject, String message) throws ThingsboardException {
try {
MimeMessage mimeMsg = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMsg, "UTF-8");
helper.setFrom(mailFrom);
helper.setTo(email);
helper.setSubject(subject);
helper.setText(message, true);
mailSender.send(helper.getMimeMessage());
} catch (Exception e) {
throw handleException(e);
}
}
protected ThingsboardException handleException(Exception exception) {
String message;
if (exception instanceof NestedRuntimeException) {
message = ((NestedRuntimeException)exception).getMostSpecificCause().getMessage();
} else {
message = exception.getMessage();
}
return new ThingsboardException(String.format("Unable to send mail: %s", message),
ThingsboardErrorCode.GENERAL);
}
}

36
application/src/main/java/org/thingsboard/server/service/mail/MailService.java

@ -0,0 +1,36 @@
/**
* Copyright © 2016 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.mail;
import org.thingsboard.server.exception.ThingsboardException;
import com.fasterxml.jackson.databind.JsonNode;
public interface MailService {
void updateMailConfiguration();
void sendTestMail(JsonNode config, String email) throws ThingsboardException;
void sendActivationEmail(String activationLink, String email) throws ThingsboardException;
void sendAccountActivatedEmail(String loginLink, String email) throws ThingsboardException;
void sendResetPasswordEmail(String passwordResetLink, String email) throws ThingsboardException;
void sendPasswordWasResetEmail(String loginLink, String email) throws ThingsboardException;
}

66
application/src/main/java/org/thingsboard/server/service/security/auth/AbstractJwtAuthenticationToken.java

@ -0,0 +1,66 @@
/**
* Copyright © 2016 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;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.model.token.RawAccessJwtToken;
public abstract class AbstractJwtAuthenticationToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = -6212297506742428406L;
private RawAccessJwtToken rawAccessToken;
private SecurityUser securityUser;
public AbstractJwtAuthenticationToken(RawAccessJwtToken unsafeToken) {
super(null);
this.rawAccessToken = unsafeToken;
this.setAuthenticated(false);
}
public AbstractJwtAuthenticationToken(SecurityUser securityUser) {
super(securityUser.getAuthorities());
this.eraseCredentials();
this.securityUser = securityUser;
super.setAuthenticated(true);
}
@Override
public void setAuthenticated(boolean authenticated) {
if (authenticated) {
throw new IllegalArgumentException(
"Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
}
super.setAuthenticated(false);
}
@Override
public Object getCredentials() {
return rawAccessToken;
}
@Override
public Object getPrincipal() {
return this.securityUser;
}
@Override
public void eraseCredentials() {
super.eraseCredentials();
this.rawAccessToken = null;
}
}

32
application/src/main/java/org/thingsboard/server/service/security/auth/JwtAuthenticationToken.java

@ -0,0 +1,32 @@
/**
* Copyright © 2016 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;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.model.token.RawAccessJwtToken;
public class JwtAuthenticationToken extends AbstractJwtAuthenticationToken {
private static final long serialVersionUID = -8487219769037942225L;
public JwtAuthenticationToken(RawAccessJwtToken unsafeToken) {
super(unsafeToken);
}
public JwtAuthenticationToken(SecurityUser securityUser) {
super(securityUser);
}
}

32
application/src/main/java/org/thingsboard/server/service/security/auth/RefreshAuthenticationToken.java

@ -0,0 +1,32 @@
/**
* Copyright © 2016 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;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.model.token.RawAccessJwtToken;
public class RefreshAuthenticationToken extends AbstractJwtAuthenticationToken {
private static final long serialVersionUID = -1311042791508924523L;
public RefreshAuthenticationToken(RawAccessJwtToken unsafeToken) {
super(unsafeToken);
}
public RefreshAuthenticationToken(SecurityUser securityUser) {
super(securityUser);
}
}

58
application/src/main/java/org/thingsboard/server/service/security/auth/jwt/JwtAuthenticationProvider.java

@ -0,0 +1,58 @@
/**
* Copyright © 2016 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.jwt;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Component;
import org.thingsboard.server.config.JwtSettings;
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 java.util.List;
import java.util.stream.Collectors;
@Component
@SuppressWarnings("unchecked")
public class JwtAuthenticationProvider implements AuthenticationProvider {
private final JwtTokenFactory tokenFactory;
@Autowired
public JwtAuthenticationProvider(JwtTokenFactory tokenFactory) {
this.tokenFactory = tokenFactory;
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
RawAccessJwtToken rawAccessToken = (RawAccessJwtToken) authentication.getCredentials();
SecurityUser securityUser = tokenFactory.parseAccessJwtToken(rawAccessToken);
return new JwtAuthenticationToken(securityUser);
}
@Override
public boolean supports(Class<?> authentication) {
return (JwtAuthenticationToken.class.isAssignableFrom(authentication));
}
}

71
application/src/main/java/org/thingsboard/server/service/security/auth/jwt/JwtTokenAuthenticationProcessingFilter.java

@ -0,0 +1,71 @@
/**
* Copyright © 2016 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.jwt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.util.matcher.RequestMatcher;
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 javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class JwtTokenAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {
private final AuthenticationFailureHandler failureHandler;
private final TokenExtractor tokenExtractor;
@Autowired
public JwtTokenAuthenticationProcessingFilter(AuthenticationFailureHandler failureHandler,
TokenExtractor tokenExtractor, RequestMatcher matcher) {
super(matcher);
this.failureHandler = failureHandler;
this.tokenExtractor = tokenExtractor;
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException, IOException, ServletException {
RawAccessJwtToken token = new RawAccessJwtToken(tokenExtractor.extract(request));
return getAuthenticationManager().authenticate(new JwtAuthenticationToken(token));
}
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
Authentication authResult) throws IOException, ServletException {
SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(authResult);
SecurityContextHolder.setContext(context);
chain.doFilter(request, response);
}
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
AuthenticationException failed) throws IOException, ServletException {
SecurityContextHolder.clearContext();
failureHandler.onAuthenticationFailure(request, response, failed);
}
}

79
application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenAuthenticationProvider.java

@ -0,0 +1,79 @@
/**
* Copyright © 2016 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.jwt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.security.UserCredentials;
import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.service.security.auth.RefreshAuthenticationToken;
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;
@Component
public class RefreshTokenAuthenticationProvider implements AuthenticationProvider {
private final JwtTokenFactory tokenFactory;
private final UserService userService;
@Autowired
public RefreshTokenAuthenticationProvider(final UserService userService, final JwtTokenFactory tokenFactory) {
this.userService = userService;
this.tokenFactory = tokenFactory;
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
Assert.notNull(authentication, "No authentication data provided");
RawAccessJwtToken rawAccessToken = (RawAccessJwtToken) authentication.getCredentials();
SecurityUser unsafeUser = tokenFactory.parseRefreshToken(rawAccessToken);
User user = userService.findUserById(unsafeUser.getId());
if (user == null) {
throw new UsernameNotFoundException("User not found by refresh token");
}
UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getId());
if (userCredentials == null) {
throw new UsernameNotFoundException("User credentials not found");
}
if (!userCredentials.isEnabled()) {
throw new DisabledException("User is not active");
}
if (user.getAuthority() == null) throw new InsufficientAuthenticationException("User has no authority assigned");
SecurityUser securityUser = new SecurityUser(user, userCredentials.isEnabled());
return new RefreshAuthenticationToken(securityUser);
}
@Override
public boolean supports(Class<?> authentication) {
return (RefreshAuthenticationToken.class.isAssignableFrom(authentication));
}
}

94
application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenProcessingFilter.java

@ -0,0 +1,94 @@
/**
* Copyright © 2016 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.jwt;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.thingsboard.server.service.security.auth.RefreshAuthenticationToken;
import org.thingsboard.server.service.security.exception.AuthMethodNotSupportedException;
import org.thingsboard.server.service.security.model.token.RawAccessJwtToken;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class RefreshTokenProcessingFilter extends AbstractAuthenticationProcessingFilter {
private static Logger logger = LoggerFactory.getLogger(RefreshTokenProcessingFilter.class);
private final AuthenticationSuccessHandler successHandler;
private final AuthenticationFailureHandler failureHandler;
private final ObjectMapper objectMapper;
public RefreshTokenProcessingFilter(String defaultProcessUrl, AuthenticationSuccessHandler successHandler,
AuthenticationFailureHandler failureHandler, ObjectMapper mapper) {
super(defaultProcessUrl);
this.successHandler = successHandler;
this.failureHandler = failureHandler;
this.objectMapper = mapper;
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException, IOException, ServletException {
if (!HttpMethod.POST.name().equals(request.getMethod())) {
if(logger.isDebugEnabled()) {
logger.debug("Authentication method not supported. Request method: " + request.getMethod());
}
throw new AuthMethodNotSupportedException("Authentication method not supported");
}
RefreshTokenRequest refreshTokenRequest;
try {
refreshTokenRequest = objectMapper.readValue(request.getReader(), RefreshTokenRequest.class);
} catch (Exception e) {
throw new AuthenticationServiceException("Invalid refresh token request payload");
}
if (StringUtils.isBlank(refreshTokenRequest.getRefreshToken())) {
throw new AuthenticationServiceException("Refresh token is not provided");
}
RawAccessJwtToken token = new RawAccessJwtToken(refreshTokenRequest.getRefreshToken());
return this.getAuthenticationManager().authenticate(new RefreshAuthenticationToken(token));
}
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
Authentication authResult) throws IOException, ServletException {
successHandler.onAuthenticationSuccess(request, response, authResult);
}
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
AuthenticationException failed) throws IOException, ServletException {
SecurityContextHolder.clearContext();
failureHandler.onAuthenticationFailure(request, response, failed);
}
}

38
application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenRepository.java

@ -0,0 +1,38 @@
/**
* Copyright © 2016 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.jwt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.model.token.JwtToken;
import org.thingsboard.server.service.security.model.token.JwtTokenFactory;
@Component
public class RefreshTokenRepository {
private final JwtTokenFactory tokenFactory;
@Autowired
public RefreshTokenRepository(final JwtTokenFactory tokenFactory) {
this.tokenFactory = tokenFactory;
}
public JwtToken requestRefreshToken(SecurityUser user) {
return tokenFactory.createRefreshToken(user);
}
}

32
application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenRequest.java

@ -0,0 +1,32 @@
/**
* Copyright © 2016 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.jwt;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
public class RefreshTokenRequest {
private String refreshToken;
@JsonCreator
public RefreshTokenRequest(@JsonProperty("refreshToken") String refreshToken) {
this.refreshToken = refreshToken;
}
public String getRefreshToken() {
return refreshToken;
}
}

45
application/src/main/java/org/thingsboard/server/service/security/auth/jwt/SkipPathRequestMatcher.java

@ -0,0 +1,45 @@
/**
* Copyright © 2016 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.jwt;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.stream.Collectors;
public class SkipPathRequestMatcher implements RequestMatcher {
private OrRequestMatcher matchers;
private RequestMatcher processingMatcher;
public SkipPathRequestMatcher(List<String> pathsToSkip, String processingPath) {
Assert.notNull(pathsToSkip);
List<RequestMatcher> m = pathsToSkip.stream().map(path -> new AntPathRequestMatcher(path)).collect(Collectors.toList());
matchers = new OrRequestMatcher(m);
processingMatcher = new AntPathRequestMatcher(processingPath);
}
@Override
public boolean matches(HttpServletRequest request) {
if (matchers.matches(request)) {
return false;
}
return processingMatcher.matches(request) ? true : false;
}
}

42
application/src/main/java/org/thingsboard/server/service/security/auth/jwt/extractor/JwtHeaderTokenExtractor.java

@ -0,0 +1,42 @@
/**
* Copyright © 2016 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.jwt.extractor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.stereotype.Component;
import org.thingsboard.server.config.ThingsboardSecurityConfiguration;
import javax.servlet.http.HttpServletRequest;
@Component(value="jwtHeaderTokenExtractor")
public class JwtHeaderTokenExtractor implements TokenExtractor {
public static String HEADER_PREFIX = "Bearer ";
@Override
public String extract(HttpServletRequest request) {
String header = request.getHeader(ThingsboardSecurityConfiguration.JWT_TOKEN_HEADER_PARAM);
if (StringUtils.isBlank(header)) {
throw new AuthenticationServiceException("Authorization header cannot be blank!");
}
if (header.length() < HEADER_PREFIX.length()) {
throw new AuthenticationServiceException("Invalid authorization header size.");
}
return header.substring(HEADER_PREFIX.length(), header.length());
}
}

43
application/src/main/java/org/thingsboard/server/service/security/auth/jwt/extractor/JwtQueryTokenExtractor.java

@ -0,0 +1,43 @@
/**
* Copyright © 2016 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.jwt.extractor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.stereotype.Component;
import org.thingsboard.server.config.ThingsboardSecurityConfiguration;
import javax.servlet.http.HttpServletRequest;
@Component(value="jwtQueryTokenExtractor")
public class JwtQueryTokenExtractor implements TokenExtractor {
@Override
public String extract(HttpServletRequest request) {
String token = null;
if (request.getParameterMap() != null && !request.getParameterMap().isEmpty()) {
String[] tokenParamValue = request.getParameterMap().get(ThingsboardSecurityConfiguration.JWT_TOKEN_QUERY_PARAM);
if (tokenParamValue != null && tokenParamValue.length == 1) {
token = tokenParamValue[0];
}
}
if (StringUtils.isBlank(token)) {
throw new AuthenticationServiceException("Authorization query parameter cannot be blank!");
}
return token;
}
}

22
application/src/main/java/org/thingsboard/server/service/security/auth/jwt/extractor/TokenExtractor.java

@ -0,0 +1,22 @@
/**
* Copyright © 2016 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.jwt.extractor;
import javax.servlet.http.HttpServletRequest;
public interface TokenExtractor {
public String extract(HttpServletRequest request);
}

38
application/src/main/java/org/thingsboard/server/service/security/auth/rest/LoginRequest.java

@ -0,0 +1,38 @@
/**
* Copyright © 2016 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.rest;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
public class LoginRequest {
private String username;
private String password;
@JsonCreator
public LoginRequest(@JsonProperty("username") String username, @JsonProperty("password") String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
}

79
application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAuthenticationProvider.java

@ -0,0 +1,79 @@
/**
* Copyright © 2016 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.rest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.security.UserCredentials;
import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.service.security.model.SecurityUser;
@Component
public class RestAuthenticationProvider implements AuthenticationProvider {
private final BCryptPasswordEncoder encoder;
private final UserService userService;
@Autowired
public RestAuthenticationProvider(final UserService userService, final BCryptPasswordEncoder encoder) {
this.userService = userService;
this.encoder = encoder;
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
Assert.notNull(authentication, "No authentication data provided");
String username = (String) authentication.getPrincipal();
String password = (String) authentication.getCredentials();
User user = userService.findUserByEmail(username);
if (user == null) {
throw new UsernameNotFoundException("User not found: " + username);
}
UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getId());
if (userCredentials == null) {
throw new UsernameNotFoundException("User credentials not found");
}
if (!userCredentials.isEnabled()) {
throw new DisabledException("User is not active");
}
if (!encoder.matches(password, userCredentials.getPassword())) {
throw new BadCredentialsException("Authentication Failed. Username or Password not valid.");
}
if (user.getAuthority() == null) throw new InsufficientAuthenticationException("User has no authority assigned");
SecurityUser securityUser = new SecurityUser(user, userCredentials.isEnabled());
return new UsernamePasswordAuthenticationToken(securityUser, null, securityUser.getAuthorities());
}
@Override
public boolean supports(Class<?> authentication) {
return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
}
}

44
application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAwareAuthenticationFailureHandler.java

@ -0,0 +1,44 @@
/**
* Copyright © 2016 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.rest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import org.thingsboard.server.exception.ThingsboardErrorResponseHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class RestAwareAuthenticationFailureHandler implements AuthenticationFailureHandler {
private final ThingsboardErrorResponseHandler errorResponseHandler;
@Autowired
public RestAwareAuthenticationFailureHandler(ThingsboardErrorResponseHandler errorResponseHandler) {
this.errorResponseHandler = errorResponseHandler;
}
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException e) throws IOException, ServletException {
errorResponseHandler.handle(e, response);
}
}

85
application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAwareAuthenticationSuccessHandler.java

@ -0,0 +1,85 @@
/**
* Copyright © 2016 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.rest;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.WebAttributes;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.model.token.JwtToken;
import org.thingsboard.server.service.security.model.token.JwtTokenFactory;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@Component
public class RestAwareAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
private final ObjectMapper mapper;
private final JwtTokenFactory tokenFactory;
private final RefreshTokenRepository refreshTokenRepository;
@Autowired
public RestAwareAuthenticationSuccessHandler(final ObjectMapper mapper, final JwtTokenFactory tokenFactory, final RefreshTokenRepository refreshTokenRepository) {
this.mapper = mapper;
this.tokenFactory = tokenFactory;
this.refreshTokenRepository = refreshTokenRepository;
}
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
SecurityUser securityUser = (SecurityUser) authentication.getPrincipal();
JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser);
JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser);
Map<String, String> tokenMap = new HashMap<String, String>();
tokenMap.put("token", accessToken.getToken());
tokenMap.put("refreshToken", refreshToken.getToken());
response.setStatus(HttpStatus.OK.value());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
mapper.writeValue(response.getWriter(), tokenMap);
clearAuthenticationAttributes(request);
}
/**
* Removes temporary authentication-related data which may have been stored
* in the session during the authentication process..
*
*/
protected final void clearAuthenticationAttributes(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session == null) {
return;
}
session.removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
}
}

93
application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestLoginProcessingFilter.java

@ -0,0 +1,93 @@
/**
* Copyright © 2016 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.rest;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.thingsboard.server.service.security.exception.AuthMethodNotSupportedException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class RestLoginProcessingFilter extends AbstractAuthenticationProcessingFilter {
private static Logger logger = LoggerFactory.getLogger(RestLoginProcessingFilter.class);
private final AuthenticationSuccessHandler successHandler;
private final AuthenticationFailureHandler failureHandler;
private final ObjectMapper objectMapper;
public RestLoginProcessingFilter(String defaultProcessUrl, AuthenticationSuccessHandler successHandler,
AuthenticationFailureHandler failureHandler, ObjectMapper mapper) {
super(defaultProcessUrl);
this.successHandler = successHandler;
this.failureHandler = failureHandler;
this.objectMapper = mapper;
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException, IOException, ServletException {
if (!HttpMethod.POST.name().equals(request.getMethod())) {
if(logger.isDebugEnabled()) {
logger.debug("Authentication method not supported. Request method: " + request.getMethod());
}
throw new AuthMethodNotSupportedException("Authentication method not supported");
}
LoginRequest loginRequest;
try {
loginRequest = objectMapper.readValue(request.getReader(), LoginRequest.class);
} catch (Exception e) {
throw new AuthenticationServiceException("Invalid login request payload");
}
if (StringUtils.isBlank(loginRequest.getUsername()) || StringUtils.isBlank(loginRequest.getPassword())) {
throw new AuthenticationServiceException("Username or Password not provided");
}
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword());
return this.getAuthenticationManager().authenticate(token);
}
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
Authentication authResult) throws IOException, ServletException {
successHandler.onAuthenticationSuccess(request, response, authResult);
}
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
AuthenticationException failed) throws IOException, ServletException {
SecurityContextHolder.clearContext();
failureHandler.onAuthenticationFailure(request, response, failed);
}
}

70
application/src/main/java/org/thingsboard/server/service/security/device/DefaultDeviceAuthService.java

@ -0,0 +1,70 @@
/**
* Copyright © 2016 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.device;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsFilter;
import org.thingsboard.server.common.transport.auth.DeviceAuthResult;
import org.thingsboard.server.common.transport.auth.DeviceAuthService;
import org.thingsboard.server.dao.device.DeviceCredentialsService;
import org.thingsboard.server.dao.device.DeviceService;
import java.util.Optional;
@Service
@Slf4j
public class DefaultDeviceAuthService implements DeviceAuthService {
@Autowired
DeviceService deviceService;
@Autowired
DeviceCredentialsService deviceCredentialsService;
@Override
public DeviceAuthResult process(DeviceCredentialsFilter credentialsFilter) {
log.trace("Lookup device credentials using filter {}", credentialsFilter);
DeviceCredentials credentials = deviceCredentialsService.findDeviceCredentialsByCredentialsId(credentialsFilter.getCredentialsId());
if (credentials != null) {
log.trace("Credentials found {}", credentials);
if (credentials.getCredentialsType() == credentialsFilter.getCredentialsType()) {
switch (credentials.getCredentialsType()) {
case ACCESS_TOKEN:
// Credentials ID matches Credentials value in this
// primitive case;
return DeviceAuthResult.of(credentials.getDeviceId());
default:
return DeviceAuthResult.of("Credentials Type is not supported yet!");
}
} else {
return DeviceAuthResult.of("Credentials Type mismatch!");
}
} else {
log.trace("Credentials not found!");
return DeviceAuthResult.of("Credentials Not Found!");
}
}
@Override
public Optional<Device> findDeviceById(DeviceId deviceId) {
return Optional.ofNullable(deviceService.findDeviceById(deviceId));
}
}

26
application/src/main/java/org/thingsboard/server/service/security/exception/AuthMethodNotSupportedException.java

@ -0,0 +1,26 @@
/**
* Copyright © 2016 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.exception;
import org.springframework.security.authentication.AuthenticationServiceException;
public class AuthMethodNotSupportedException extends AuthenticationServiceException {
private static final long serialVersionUID = 3705043083010304496L;
public AuthMethodNotSupportedException(String msg) {
super(msg);
}
}

38
application/src/main/java/org/thingsboard/server/service/security/exception/JwtExpiredTokenException.java

@ -0,0 +1,38 @@
/**
* Copyright © 2016 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.exception;
import org.springframework.security.core.AuthenticationException;
import org.thingsboard.server.service.security.model.token.JwtToken;
public class JwtExpiredTokenException extends AuthenticationException {
private static final long serialVersionUID = -5959543783324224864L;
private JwtToken token;
public JwtExpiredTokenException(String msg) {
super(msg);
}
public JwtExpiredTokenException(JwtToken token, String msg, Throwable t) {
super(msg, t);
this.token = token;
}
public String token() {
return this.token.getToken();
}
}

64
application/src/main/java/org/thingsboard/server/service/security/model/SecurityUser.java

@ -0,0 +1,64 @@
/**
* Copyright © 2016 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.model;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.UserId;
import java.util.Arrays;
import java.util.Collection;
import java.util.stream.Collectors;
public class SecurityUser extends User {
private static final long serialVersionUID = -797397440703066079L;
private Collection<GrantedAuthority> authorities;
private boolean enabled;
public SecurityUser() {
super();
}
public SecurityUser(UserId id) {
super(id);
}
public SecurityUser(User user, boolean enabled) {
super(user);
this.enabled = enabled;
}
public Collection<? extends GrantedAuthority> getAuthorities() {
if (authorities == null) {
authorities = Arrays.asList(SecurityUser.this.getAuthority()).stream()
.map(authority -> new SimpleGrantedAuthority(authority.name()))
.collect(Collectors.toList());
}
return authorities;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}

38
application/src/main/java/org/thingsboard/server/service/security/model/token/AccessJwtToken.java

@ -0,0 +1,38 @@
/**
* Copyright © 2016 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.model.token;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.jsonwebtoken.Claims;
public final class AccessJwtToken implements JwtToken {
private final String rawToken;
@JsonIgnore
private Claims claims;
protected AccessJwtToken(final String token, Claims claims) {
this.rawToken = token;
this.claims = claims;
}
public String getToken() {
return this.rawToken;
}
public Claims getClaims() {
return claims;
}
}

20
application/src/main/java/org/thingsboard/server/service/security/model/token/JwtToken.java

@ -0,0 +1,20 @@
/**
* Copyright © 2016 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.model.token;
public interface JwtToken {
String getToken();
}

158
application/src/main/java/org/thingsboard/server/service/security/model/token/JwtTokenFactory.java

@ -0,0 +1,158 @@
/**
* Copyright © 2016 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.model.token;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
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.data.security.Authority;
import org.thingsboard.server.config.JwtSettings;
import org.thingsboard.server.service.security.model.SecurityUser;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
@Component
public class JwtTokenFactory {
private static final String SCOPES = "scopes";
private static final String USER_ID = "userId";
private static final String FIRST_NAME = "firstName";
private static final String LAST_NAME = "lastName";
private static final String ENABLED = "enabled";
private static final String TENANT_ID = "tenantId";
private static final String CUSTOMER_ID = "customerId";
private final JwtSettings settings;
@Autowired
public JwtTokenFactory(JwtSettings settings) {
this.settings = settings;
}
/**
* Factory method for issuing new JWT Tokens.
*/
public AccessJwtToken createAccessJwtToken(SecurityUser securityUser) {
if (StringUtils.isBlank(securityUser.getEmail()))
throw new IllegalArgumentException("Cannot create JWT Token without username/email");
if (securityUser.getAuthority() == null)
throw new IllegalArgumentException("User doesn't have any privileges");
Claims claims = Jwts.claims().setSubject(securityUser.getEmail());
claims.put(SCOPES, securityUser.getAuthorities().stream().map(s -> s.getAuthority()).collect(Collectors.toList()));
claims.put(USER_ID, securityUser.getId().getId().toString());
claims.put(FIRST_NAME, securityUser.getFirstName());
claims.put(LAST_NAME, securityUser.getLastName());
claims.put(ENABLED, securityUser.isEnabled());
if (securityUser.getTenantId() != null) {
claims.put(TENANT_ID, securityUser.getTenantId().getId().toString());
}
if (securityUser.getCustomerId() != null) {
claims.put(CUSTOMER_ID, securityUser.getCustomerId().getId().toString());
}
DateTime currentTime = new DateTime();
String token = Jwts.builder()
.setClaims(claims)
.setIssuer(settings.getTokenIssuer())
.setIssuedAt(currentTime.toDate())
.setExpiration(currentTime.plusSeconds(settings.getTokenExpirationTime()).toDate())
.signWith(SignatureAlgorithm.HS512, settings.getTokenSigningKey())
.compact();
return new AccessJwtToken(token, claims);
}
public SecurityUser parseAccessJwtToken(RawAccessJwtToken rawAccessToken) {
Jws<Claims> jwsClaims = rawAccessToken.parseClaims(settings.getTokenSigningKey());
Claims claims = jwsClaims.getBody();
String subject = claims.getSubject();
List<String> scopes = claims.get(SCOPES, List.class);
if (scopes == null || scopes.isEmpty()) {
throw new IllegalArgumentException("JWT Token doesn't have any scopes");
}
SecurityUser securityUser = new SecurityUser(new UserId(UUID.fromString(claims.get(USER_ID, String.class))));
securityUser.setEmail(subject);
securityUser.setAuthority(Authority.parse(scopes.get(0)));
securityUser.setFirstName(claims.get(FIRST_NAME, String.class));
securityUser.setLastName(claims.get(LAST_NAME, String.class));
securityUser.setEnabled(claims.get(ENABLED, Boolean.class));
String tenantId = claims.get(TENANT_ID, String.class);
if (tenantId != null) {
securityUser.setTenantId(new TenantId(UUID.fromString(tenantId)));
}
String customerId = claims.get(CUSTOMER_ID, String.class);
if (customerId != null) {
securityUser.setCustomerId(new CustomerId(UUID.fromString(customerId)));
}
return securityUser;
}
public JwtToken createRefreshToken(SecurityUser securityUser) {
if (StringUtils.isBlank(securityUser.getEmail())) {
throw new IllegalArgumentException("Cannot create JWT Token without username/email");
}
DateTime currentTime = new DateTime();
Claims claims = Jwts.claims().setSubject(securityUser.getEmail());
claims.put(SCOPES, Arrays.asList(Authority.REFRESH_TOKEN.name()));
claims.put(USER_ID, securityUser.getId().getId().toString());
String token = Jwts.builder()
.setClaims(claims)
.setIssuer(settings.getTokenIssuer())
.setId(UUID.randomUUID().toString())
.setIssuedAt(currentTime.toDate())
.setExpiration(currentTime.plusSeconds(settings.getRefreshTokenExpTime()).toDate())
.signWith(SignatureAlgorithm.HS512, settings.getTokenSigningKey())
.compact();
return new AccessJwtToken(token, claims);
}
public SecurityUser parseRefreshToken(RawAccessJwtToken rawAccessToken) {
Jws<Claims> jwsClaims = rawAccessToken.parseClaims(settings.getTokenSigningKey());
Claims claims = jwsClaims.getBody();
String subject = claims.getSubject();
List<String> scopes = claims.get(SCOPES, List.class);
if (scopes == null || scopes.isEmpty()) {
throw new IllegalArgumentException("Refresh Token doesn't have any scopes");
}
if (!scopes.get(0).equals(Authority.REFRESH_TOKEN.name())) {
throw new IllegalArgumentException("Invalid Refresh Token scope");
}
SecurityUser securityUser = new SecurityUser(new UserId(UUID.fromString(claims.get(USER_ID, String.class))));
securityUser.setEmail(subject);
return securityUser;
}
}

56
application/src/main/java/org/thingsboard/server/service/security/model/token/RawAccessJwtToken.java

@ -0,0 +1,56 @@
/**
* Copyright © 2016 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.model.token;
import io.jsonwebtoken.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.BadCredentialsException;
import org.thingsboard.server.service.security.exception.JwtExpiredTokenException;
public class RawAccessJwtToken implements JwtToken {
private static Logger logger = LoggerFactory.getLogger(RawAccessJwtToken.class);
private String token;
public RawAccessJwtToken(String token) {
this.token = token;
}
/**
* Parses and validates JWT Token signature.
*
* @throws BadCredentialsException
* @throws JwtExpiredTokenException
*
*/
public Jws<Claims> parseClaims(String signingKey) {
try {
return Jwts.parser().setSigningKey(signingKey).parseClaimsJws(this.token);
} catch (UnsupportedJwtException | MalformedJwtException | IllegalArgumentException | SignatureException ex) {
logger.error("Invalid JWT Token", ex);
throw new BadCredentialsException("Invalid JWT token: ", ex);
} catch (ExpiredJwtException expiredEx) {
logger.info("JWT Token is expired", expiredEx);
throw new JwtExpiredTokenException(this, "JWT Token expired", expiredEx);
}
}
@Override
public String getToken() {
return token;
}
}

176
dao/pom.xml

@ -0,0 +1,176 @@
<!--
Copyright © 2016 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>0.0.1-SNAPSHOT</version>
<artifactId>server</artifactId>
</parent>
<groupId>org.thingsboard.server</groupId>
<artifactId>dao</artifactId>
<packaging>jar</packaging>
<name>Thingsboard Server DAO Layer</name>
<url>http://thingsboard.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<main.dir>${basedir}/..</main.dir>
</properties>
<dependencies>
<dependency>
<groupId>org.thingsboard.server.common</groupId>
<artifactId>data</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>commons-validator</groupId>
<artifactId>commons-validator</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.github.fge</groupId>
<artifactId>json-schema-validator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
<dependency>
<groupId>com.datastax.cassandra</groupId>
<artifactId>cassandra-driver-core</artifactId>
</dependency>
<dependency>
<groupId>com.datastax.cassandra</groupId>
<artifactId>cassandra-driver-mapping</artifactId>
</dependency>
<dependency>
<groupId>com.datastax.cassandra</groupId>
<artifactId>cassandra-driver-extras</artifactId>
</dependency>
<dependency>
<groupId>io.takari.junit</groupId>
<artifactId>takari-cpsuite</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>org.cassandraunit</groupId>
<artifactId>cassandra-unit</artifactId>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-x-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast-zookeeper</artifactId>
</dependency>
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast</artifactId>
</dependency>
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast-spring</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surfire.version}</version>
<configuration>
<includes>
<include>**/*TestSuite.java</include>
</includes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>${jar-plugin.version}</version>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

93
dao/src/main/java/org/thingsboard/server/dao/AbstractDao.java

@ -0,0 +1,93 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao;
import com.datastax.driver.core.*;
import com.datastax.driver.core.exceptions.CodecNotFoundException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.thingsboard.server.dao.cassandra.CassandraCluster;
import org.thingsboard.server.dao.model.type.*;
@Slf4j
public abstract class AbstractDao {
@Autowired
protected CassandraCluster cluster;
private Session session;
private ConsistencyLevel defaultReadLevel;
private ConsistencyLevel defaultWriteLevel;
protected Session getSession() {
if (session == null) {
session = cluster.getSession();
defaultReadLevel = cluster.getDefaultReadConsistencyLevel();
defaultWriteLevel = cluster.getDefaultWriteConsistencyLevel();
CodecRegistry registry = session.getCluster().getConfiguration().getCodecRegistry();
registerCodecIfNotFound(registry, new JsonCodec());
registerCodecIfNotFound(registry, new DeviceCredentialsTypeCodec());
registerCodecIfNotFound(registry, new AuthorityCodec());
registerCodecIfNotFound(registry, new ComponentLifecycleStateCodec());
registerCodecIfNotFound(registry, new ComponentTypeCodec());
registerCodecIfNotFound(registry, new ComponentScopeCodec());
registerCodecIfNotFound(registry, new EntityTypeCodec());
}
return session;
}
private void registerCodecIfNotFound(CodecRegistry registry, TypeCodec<?> codec) {
try {
registry.codecFor(codec.getCqlType(), codec.getJavaType());
} catch (CodecNotFoundException e) {
registry.register(codec);
}
}
protected ResultSet executeRead(Statement statement) {
return execute(statement, defaultReadLevel);
}
protected ResultSet executeWrite(Statement statement) {
return execute(statement, defaultWriteLevel);
}
protected ResultSetFuture executeAsyncRead(Statement statement) {
return executeAsync(statement, defaultReadLevel);
}
protected ResultSetFuture executeAsyncWrite(Statement statement) {
return executeAsync(statement, defaultWriteLevel);
}
private ResultSet execute(Statement statement, ConsistencyLevel level) {
log.debug("Execute cassandra statement {}", statement);
if (statement.getConsistencyLevel() == null) {
statement.setConsistencyLevel(level);
}
return getSession().execute(statement);
}
private ResultSetFuture executeAsync(Statement statement, ConsistencyLevel level) {
log.debug("Execute cassandra async statement {}", statement);
if (statement.getConsistencyLevel() == null) {
statement.setConsistencyLevel(level);
}
return getSession().executeAsync(statement);
}
}

114
dao/src/main/java/org/thingsboard/server/dao/AbstractModelDao.java

@ -0,0 +1,114 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Statement;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Select;
import com.datastax.driver.core.utils.UUIDs;
import com.datastax.driver.mapping.Mapper;
import com.datastax.driver.mapping.Result;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.dao.model.BaseEntity;
import org.thingsboard.server.dao.model.wrapper.EntityResultSet;
import org.thingsboard.server.dao.model.ModelConstants;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
import static com.datastax.driver.core.querybuilder.QueryBuilder.lt;
import static com.datastax.driver.core.querybuilder.QueryBuilder.select;
@Slf4j
public abstract class AbstractModelDao<T extends BaseEntity<?>> extends AbstractDao implements Dao<T> {
protected abstract Class<T> getColumnFamilyClass();
protected abstract String getColumnFamilyName();
protected Mapper<T> getMapper() {
return cluster.getMapper(getColumnFamilyClass());
}
protected List<T> findListByStatement(Statement statement) {
List<T> list = Collections.emptyList();
if (statement != null) {
statement.setConsistencyLevel(cluster.getDefaultReadConsistencyLevel());
ResultSet resultSet = getSession().execute(statement);
Result<T> result = getMapper().map(resultSet);
if (result != null) {
list = result.all();
}
}
return list;
}
protected T findOneByStatement(Statement statement) {
T object = null;
if (statement != null) {
statement.setConsistencyLevel(cluster.getDefaultReadConsistencyLevel());
ResultSet resultSet = getSession().execute(statement);
Result<T> result = getMapper().map(resultSet);
if (result != null) {
object = result.one();
}
}
return object;
}
protected Statement getSaveQuery(T dto) {
return getMapper().saveQuery(dto);
}
protected EntityResultSet<T> saveWithResult(T entity) {
log.debug("Save entity {}", entity);
if (entity.getId() == null) {
entity.setId(UUIDs.timeBased());
} else {
removeById(entity.getId());
}
Statement saveStatement = getSaveQuery(entity);
saveStatement.setConsistencyLevel(cluster.getDefaultWriteConsistencyLevel());
ResultSet resultSet = executeWrite(saveStatement);
return new EntityResultSet<>(resultSet, entity);
}
public T save(T entity) {
return saveWithResult(entity).getEntity();
}
public T findById(UUID key) {
log.debug("Get entity by key {}", key);
Select.Where query = select().from(getColumnFamilyName()).where(eq(ModelConstants.ID_PROPERTY, key));
log.trace("Execute query {}", query);
return findOneByStatement(query);
}
public ResultSet removeById(UUID key) {
Statement delete = QueryBuilder.delete().all().from(getColumnFamilyName()).where(eq(ModelConstants.ID_PROPERTY, key));
log.debug("Remove request: {}", delete.toString());
return getSession().execute(delete);
}
public List<T> find() {
log.debug("Get all entities from column family {}", getColumnFamilyName());
return findListByStatement(QueryBuilder.select().all().from(getColumnFamilyName()).setConsistencyLevel(cluster.getDefaultReadConsistencyLevel()));
}
}

78
dao/src/main/java/org/thingsboard/server/dao/AbstractSearchTextDao.java

@ -0,0 +1,78 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao;
import com.datastax.driver.core.querybuilder.Clause;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Select;
import com.datastax.driver.core.querybuilder.Select.Where;
import org.apache.commons.lang3.StringUtils;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.dao.model.SearchTextEntity;
import org.thingsboard.server.dao.model.ModelConstants;
import java.util.List;
import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
import static com.datastax.driver.core.querybuilder.QueryBuilder.gt;
import static com.datastax.driver.core.querybuilder.QueryBuilder.gte;
import static com.datastax.driver.core.querybuilder.QueryBuilder.lt;
import static com.datastax.driver.core.querybuilder.QueryBuilder.select;
public abstract class AbstractSearchTextDao<T extends SearchTextEntity<?>> extends AbstractModelDao<T> {
public T save(T entity) {
entity.setSearchText(entity.getSearchTextSource().toLowerCase());
return super.save(entity);
}
protected List<T> findPageWithTextSearch(String searchView, List<Clause> clauses, TextPageLink pageLink) {
Select select = select().from(searchView);
Where query = select.where();
for (Clause clause : clauses) {
query.and(clause);
}
query.limit(pageLink.getLimit());
if (!StringUtils.isEmpty(pageLink.getTextOffset())) {
query.and(eq(ModelConstants.SEARCH_TEXT_PROPERTY, pageLink.getTextOffset()));
query.and(QueryBuilder.lt(ModelConstants.ID_PROPERTY, pageLink.getIdOffset()));
List<T> result = findListByStatement(query);
if (result.size() < pageLink.getLimit()) {
select = select().from(searchView);
query = select.where();
for (Clause clause : clauses) {
query.and(clause);
}
query.and(QueryBuilder.gt(ModelConstants.SEARCH_TEXT_PROPERTY, pageLink.getTextOffset()));
if (!StringUtils.isEmpty(pageLink.getTextSearch())) {
query.and(QueryBuilder.lt(ModelConstants.SEARCH_TEXT_PROPERTY, pageLink.getTextSearchBound()));
}
int limit = pageLink.getLimit() - result.size();
query.limit(limit);
result.addAll(findListByStatement(query));
}
return result;
} else if (!StringUtils.isEmpty(pageLink.getTextSearch())) {
query.and(QueryBuilder.gte(ModelConstants.SEARCH_TEXT_PROPERTY, pageLink.getTextSearch()));
query.and(QueryBuilder.lt(ModelConstants.SEARCH_TEXT_PROPERTY, pageLink.getTextSearchBound()));
return findListByStatement(query);
} else {
return findListByStatement(query);
}
}
}

90
dao/src/main/java/org/thingsboard/server/dao/AbstractSearchTimeDao.java

@ -0,0 +1,90 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao;
import com.datastax.driver.core.querybuilder.Clause;
import com.datastax.driver.core.querybuilder.Ordering;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Select;
import com.datastax.driver.core.querybuilder.Select.Where;
import com.datastax.driver.core.utils.UUIDs;
import org.apache.commons.lang3.StringUtils;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.dao.model.BaseEntity;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.model.SearchTextEntity;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
import static com.datastax.driver.core.querybuilder.QueryBuilder.select;
public abstract class AbstractSearchTimeDao<T extends BaseEntity<?>> extends AbstractModelDao<T> {
protected List<T> findPageWithTimeSearch(String searchView, List<Clause> clauses, TimePageLink pageLink) {
return findPageWithTimeSearch(searchView, clauses, Collections.emptyList(), pageLink);
}
protected List<T> findPageWithTimeSearch(String searchView, List<Clause> clauses, Ordering ordering, TimePageLink pageLink) {
return findPageWithTimeSearch(searchView, clauses, Collections.singletonList(ordering), pageLink);
}
protected List<T> findPageWithTimeSearch(String searchView, List<Clause> clauses, List<Ordering> topLevelOrderings, TimePageLink pageLink) {
Select select = select().from(searchView);
Where query = select.where();
for (Clause clause : clauses) {
query.and(clause);
}
query.limit(pageLink.getLimit());
if (pageLink.isAscOrder()) {
if (pageLink.getIdOffset() != null) {
query.and(QueryBuilder.gt(ModelConstants.ID_PROPERTY, pageLink.getIdOffset()));
} else if (pageLink.getStartTime() != null) {
final UUID startOf = UUIDs.startOf(pageLink.getStartTime());
query.and(QueryBuilder.gte(ModelConstants.ID_PROPERTY, startOf));
}
if (pageLink.getEndTime() != null) {
final UUID endOf = UUIDs.endOf(pageLink.getEndTime());
query.and(QueryBuilder.lte(ModelConstants.ID_PROPERTY, endOf));
}
} else {
if (pageLink.getIdOffset() != null) {
query.and(QueryBuilder.lt(ModelConstants.ID_PROPERTY, pageLink.getIdOffset()));
} else if (pageLink.getEndTime() != null) {
final UUID endOf = UUIDs.endOf(pageLink.getEndTime());
query.and(QueryBuilder.lte(ModelConstants.ID_PROPERTY, endOf));
}
if (pageLink.getStartTime() != null) {
final UUID startOf = UUIDs.startOf(pageLink.getStartTime());
query.and(QueryBuilder.gte(ModelConstants.ID_PROPERTY, startOf));
}
}
List<Ordering> orderings = new ArrayList<>(topLevelOrderings);
if (pageLink.isAscOrder()) {
orderings.add(QueryBuilder.asc(ModelConstants.ID_PROPERTY));
} else {
orderings.add(QueryBuilder.desc(ModelConstants.ID_PROPERTY));
}
query.orderBy(orderings.toArray(new Ordering[orderings.size()]));
return findListByStatement(query);
}
}

33
dao/src/main/java/org/thingsboard/server/dao/Dao.java

@ -0,0 +1,33 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao;
import com.datastax.driver.core.ResultSet;
import java.util.List;
import java.util.UUID;
public interface Dao<T> {
List<T> find();
T findById(UUID id);
T save(T t);
ResultSet removeById(UUID id);
}

61
dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java

@ -0,0 +1,61 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import org.thingsboard.server.common.data.id.UUIDBased;
import org.thingsboard.server.dao.model.ToData;
public abstract class DaoUtil {
private DaoUtil() {
}
public static <T> List<T> convertDataList(Collection<? extends ToData<T>> toDataList) {
List<T> list = Collections.emptyList();
if (toDataList != null && !toDataList.isEmpty()) {
list = new ArrayList<>();
for (ToData<T> object : toDataList) {
list.add(object.toData());
}
}
return list;
}
public static <T> T getData(ToData<T> data) {
T object = null;
if (data != null) {
object = data.toData();
}
return object;
}
public static UUID getId(UUIDBased idBased) {
UUID id = null;
if (idBased != null) {
id = idBased.getId();
}
return id;
}
}

159
dao/src/main/java/org/thingsboard/server/dao/cassandra/CassandraCluster.java

@ -0,0 +1,159 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.cassandra;
import com.datastax.driver.core.Cluster;
import com.datastax.driver.core.ConsistencyLevel;
import com.datastax.driver.core.ProtocolOptions.Compression;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.exceptions.NoHostAvailableException;
import com.datastax.driver.mapping.Mapper;
import com.datastax.driver.mapping.MappingManager;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.thingsboard.server.dao.exception.DatabaseException;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.Closeable;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.StringTokenizer;
@Component
@Slf4j
@Data
public class CassandraCluster {
private static final String COMMA = ",";
private static final String COLON = ":";
@Value("${cassandra.cluster_name}")
private String clusterName;
@Value("${cassandra.keyspace_name}")
private String keyspaceName;
@Value("${cassandra.url}")
private String url;
@Value("${cassandra.compression}")
private String compression;
@Value("${cassandra.ssl}")
private Boolean ssl;
@Value("${cassandra.jmx}")
private Boolean jmx;
@Value("${cassandra.metrics}")
private Boolean metrics;
@Value("${cassandra.credentials}")
private Boolean credentials;
@Value("${cassandra.username}")
private String username;
@Value("${cassandra.password}")
private String password;
@Value("${cassandra.init_timeout_ms}")
private long initTimeout;
@Value("${cassandra.init_retry_interval_ms}")
private long initRetryInterval;
@Autowired
private CassandraSocketOptions socketOpts;
@Autowired
private CassandraQueryOptions queryOpts;
private Cluster cluster;
private Session session;
private MappingManager mappingManager;
public <T> Mapper<T> getMapper(Class<T> clazz) {
return mappingManager.mapper(clazz);
}
@PostConstruct
public void init() {
long endTime = System.currentTimeMillis() + initTimeout;
while (System.currentTimeMillis() < endTime) {
try {
Cluster.Builder builder = Cluster.builder()
.addContactPointsWithPorts(getContactPoints(url))
.withClusterName(clusterName)
.withSocketOptions(socketOpts.getOpts());
builder.withQueryOptions(queryOpts.getOpts());
builder.withCompression(StringUtils.isEmpty(compression) ? Compression.NONE : Compression.valueOf(compression.toUpperCase()));
if (ssl) {
builder.withSSL();
}
if (!jmx) {
builder.withoutJMXReporting();
}
if (!metrics) {
builder.withoutMetrics();
}
if (credentials) {
builder.withCredentials(username, password);
}
cluster = builder.build();
cluster.init();
session = cluster.connect(keyspaceName);
mappingManager = new MappingManager(session);
break;
} catch (Exception e) {
log.warn("Failed to initialize cassandra cluster due to {}. Will retry in {} ms", e.getMessage(), initRetryInterval);
try {
Thread.sleep(initRetryInterval);
} catch (InterruptedException ie) {
log.warn("Failed to wait until retry", ie);
}
}
}
}
@PreDestroy
public void close() {
if (cluster != null) {
cluster.close();
}
}
private List<InetSocketAddress> getContactPoints(String url) {
List<InetSocketAddress> result;
if (StringUtils.isBlank(url)) {
result = Collections.emptyList();
} else {
result = new ArrayList<>();
for (String hostPort : url.split(COMMA)) {
String host = hostPort.split(COLON)[0];
Integer port = Integer.valueOf(hostPort.split(COLON)[1]);
result.add(new InetSocketAddress(host, port));
}
}
return result;
}
public ConsistencyLevel getDefaultReadConsistencyLevel() {
return queryOpts.getDefaultReadConsistencyLevel();
}
public ConsistencyLevel getDefaultWriteConsistencyLevel() {
return queryOpts.getDefaultWriteConsistencyLevel();
}
}

74
dao/src/main/java/org/thingsboard/server/dao/cassandra/CassandraQueryOptions.java

@ -0,0 +1,74 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.cassandra;
import com.datastax.driver.core.ConsistencyLevel;
import com.datastax.driver.core.QueryOptions;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.annotation.PostConstruct;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
@Component
@Configuration
@Data
public class CassandraQueryOptions {
@Value("${cassandra.query.default_fetch_size}")
private Integer defaultFetchSize;
@Value("${cassandra.query.read_consistency_level}")
private String readConsistencyLevel;
@Value("${cassandra.query.write_consistency_level}")
private String writeConsistencyLevel;
private QueryOptions opts;
private ConsistencyLevel defaultReadConsistencyLevel;
private ConsistencyLevel defaultWriteConsistencyLevel;
@PostConstruct
public void initOpts(){
opts = new QueryOptions();
opts.setFetchSize(defaultFetchSize);
}
protected ConsistencyLevel getDefaultReadConsistencyLevel() {
if (defaultReadConsistencyLevel == null) {
if (readConsistencyLevel != null) {
defaultReadConsistencyLevel = ConsistencyLevel.valueOf(readConsistencyLevel.toUpperCase());
} else {
defaultReadConsistencyLevel = ConsistencyLevel.ONE;
}
}
return defaultReadConsistencyLevel;
}
protected ConsistencyLevel getDefaultWriteConsistencyLevel() {
if (defaultWriteConsistencyLevel == null) {
if (writeConsistencyLevel != null) {
defaultWriteConsistencyLevel = ConsistencyLevel.valueOf(writeConsistencyLevel.toUpperCase());
} else {
defaultWriteConsistencyLevel = ConsistencyLevel.ONE;
}
}
return defaultWriteConsistencyLevel;
}
}

75
dao/src/main/java/org/thingsboard/server/dao/cassandra/CassandraSocketOptions.java

@ -0,0 +1,75 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.cassandra;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import com.datastax.driver.core.SocketOptions;
import javax.annotation.PostConstruct;
@Component
@Configuration
@Data
public class CassandraSocketOptions {
@Value("${cassandra.socket.connect_timeout}")
private int connectTimeoutMillis;
@Value("${cassandra.socket.read_timeout}")
private int readTimeoutMillis;
@Value("${cassandra.socket.keep_alive}")
private Boolean keepAlive;
@Value("${cassandra.socket.reuse_address}")
private Boolean reuseAddress;
@Value("${cassandra.socket.so_linger}")
private Integer soLinger;
@Value("${cassandra.socket.tcp_no_delay}")
private Boolean tcpNoDelay;
@Value("${cassandra.socket.receive_buffer_size}")
private Integer receiveBufferSize;
@Value("${cassandra.socket.send_buffer_size}")
private Integer sendBufferSize;
private SocketOptions opts;
@PostConstruct
public void initOpts() {
opts = new SocketOptions();
opts.setConnectTimeoutMillis(connectTimeoutMillis);
opts.setReadTimeoutMillis(readTimeoutMillis);
if (keepAlive != null) {
opts.setKeepAlive(keepAlive);
}
if (reuseAddress != null) {
opts.setReuseAddress(reuseAddress);
}
if (soLinger != null) {
opts.setSoLinger(soLinger);
}
if (tcpNoDelay != null) {
opts.setTcpNoDelay(tcpNoDelay);
}
if (receiveBufferSize != null) {
opts.setReceiveBufferSize(receiveBufferSize);
}
if (sendBufferSize != null) {
opts.setSendBufferSize(sendBufferSize);
}
}
}

169
dao/src/main/java/org/thingsboard/server/dao/component/BaseComponentDescriptorDao.java

@ -0,0 +1,169 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.component;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Statement;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Select;
import com.datastax.driver.core.utils.UUIDs;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.id.ComponentDescriptorId;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
import org.thingsboard.server.common.data.plugin.ComponentScope;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.dao.AbstractSearchTextDao;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.model.ComponentDescriptorEntity;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
import static com.datastax.driver.core.querybuilder.QueryBuilder.select;
/**
* @author Andrew Shvayka
*/
@Component
@Slf4j
public class BaseComponentDescriptorDao extends AbstractSearchTextDao<ComponentDescriptorEntity> implements ComponentDescriptorDao {
@Override
protected Class<ComponentDescriptorEntity> getColumnFamilyClass() {
return ComponentDescriptorEntity.class;
}
@Override
protected String getColumnFamilyName() {
return ModelConstants.COMPONENT_DESCRIPTOR_COLUMN_FAMILY_NAME;
}
@Override
public Optional<ComponentDescriptorEntity> save(ComponentDescriptor component) {
ComponentDescriptorEntity entity = new ComponentDescriptorEntity(component);
log.debug("Save component entity [{}]", entity);
Optional<ComponentDescriptorEntity> result = saveIfNotExist(entity);
if (log.isTraceEnabled()) {
log.trace("Saved result: [{}] for component entity [{}]", result.isPresent(), result.orElse(null));
} else {
log.debug("Saved result: [{}]", result.isPresent());
}
return result;
}
@Override
public ComponentDescriptorEntity findById(ComponentDescriptorId componentId) {
log.debug("Search component entity by id [{}]", componentId);
ComponentDescriptorEntity entity = super.findById(componentId.getId());
if (log.isTraceEnabled()) {
log.trace("Search result: [{}] for component entity [{}]", entity != null, entity);
} else {
log.debug("Search result: [{}]", entity != null);
}
return entity;
}
@Override
public ComponentDescriptorEntity findByClazz(String clazz) {
log.debug("Search component entity by clazz [{}]", clazz);
Select.Where query = select().from(getColumnFamilyName()).where(eq(ModelConstants.COMPONENT_DESCRIPTOR_CLASS_PROPERTY, clazz));
log.trace("Execute query [{}]", query);
ComponentDescriptorEntity entity = findOneByStatement(query);
if (log.isTraceEnabled()) {
log.trace("Search result: [{}] for component entity [{}]", entity != null, entity);
} else {
log.debug("Search result: [{}]", entity != null);
}
return entity;
}
@Override
public List<ComponentDescriptorEntity> findByTypeAndPageLink(ComponentType type, TextPageLink pageLink) {
log.debug("Try to find component by type [{}] and pageLink [{}]", type, pageLink);
List<ComponentDescriptorEntity> entities = findPageWithTextSearch(ModelConstants.COMPONENT_DESCRIPTOR_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
Arrays.asList(eq(ModelConstants.COMPONENT_DESCRIPTOR_TYPE_PROPERTY, type.name())), pageLink);
if (log.isTraceEnabled()) {
log.trace("Search result: [{}]", Arrays.toString(entities.toArray()));
} else {
log.debug("Search result: [{}]", entities.size());
}
return entities;
}
@Override
public List<ComponentDescriptorEntity> findByScopeAndTypeAndPageLink(ComponentScope scope, ComponentType type, TextPageLink pageLink) {
log.debug("Try to find component by scope [{}] and type [{}] and pageLink [{}]", scope, type, pageLink);
List<ComponentDescriptorEntity> entities = findPageWithTextSearch(ModelConstants.COMPONENT_DESCRIPTOR_BY_SCOPE_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
Arrays.asList(eq(ModelConstants.COMPONENT_DESCRIPTOR_TYPE_PROPERTY, type.name()),
eq(ModelConstants.COMPONENT_DESCRIPTOR_SCOPE_PROPERTY, scope.name())), pageLink);
if (log.isTraceEnabled()) {
log.trace("Search result: [{}]", Arrays.toString(entities.toArray()));
} else {
log.debug("Search result: [{}]", entities.size());
}
return entities;
}
public ResultSet removeById(UUID key) {
Statement delete = QueryBuilder.delete().all().from(ModelConstants.COMPONENT_DESCRIPTOR_BY_ID).where(eq(ModelConstants.ID_PROPERTY, key));
log.debug("Remove request: {}", delete.toString());
return getSession().execute(delete);
}
@Override
public void deleteById(ComponentDescriptorId id) {
log.debug("Delete plugin meta-data entity by id [{}]", id);
ResultSet resultSet = removeById(id.getId());
log.debug("Delete result: [{}]", resultSet.wasApplied());
}
@Override
public void deleteByClazz(String clazz) {
log.debug("Delete plugin meta-data entity by id [{}]", clazz);
Statement delete = QueryBuilder.delete().all().from(getColumnFamilyName()).where(eq(ModelConstants.COMPONENT_DESCRIPTOR_CLASS_PROPERTY, clazz));
log.debug("Remove request: {}", delete.toString());
ResultSet resultSet = getSession().execute(delete);
log.debug("Delete result: [{}]", resultSet.wasApplied());
}
private Optional<ComponentDescriptorEntity> saveIfNotExist(ComponentDescriptorEntity entity) {
if (entity.getId() == null) {
entity.setId(UUIDs.timeBased());
}
ResultSet rs = executeRead(QueryBuilder.insertInto(getColumnFamilyName())
.value(ModelConstants.ID_PROPERTY, entity.getId())
.value(ModelConstants.COMPONENT_DESCRIPTOR_NAME_PROPERTY, entity.getName())
.value(ModelConstants.COMPONENT_DESCRIPTOR_CLASS_PROPERTY, entity.getClazz())
.value(ModelConstants.COMPONENT_DESCRIPTOR_TYPE_PROPERTY, entity.getType())
.value(ModelConstants.COMPONENT_DESCRIPTOR_SCOPE_PROPERTY, entity.getScope())
.value(ModelConstants.COMPONENT_DESCRIPTOR_CONFIGURATION_DESCRIPTOR_PROPERTY, entity.getConfigurationDescriptor())
.value(ModelConstants.COMPONENT_DESCRIPTOR_ACTIONS_PROPERTY, entity.getActions())
.value(ModelConstants.SEARCH_TEXT_PROPERTY, entity.getSearchText())
.ifNotExists()
);
if (rs.wasApplied()) {
return Optional.of(entity);
} else {
return Optional.empty();
}
}
}

133
dao/src/main/java/org/thingsboard/server/dao/component/BaseComponentDescriptorService.java

@ -0,0 +1,133 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.component;
import com.fasterxml.jackson.databind.JsonNode;
import com.github.fge.jsonschema.core.exceptions.ProcessingException;
import com.github.fge.jsonschema.core.report.ProcessingReport;
import com.github.fge.jsonschema.main.JsonSchemaFactory;
import com.github.fge.jsonschema.main.JsonValidator;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.id.ComponentDescriptorId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
import org.thingsboard.server.common.data.plugin.ComponentScope;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.model.ComponentDescriptorEntity;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.Validator;
import java.util.List;
import java.util.Optional;
import static org.thingsboard.server.dao.DaoUtil.convertDataList;
import static org.thingsboard.server.dao.DaoUtil.getData;
/**
* @author Andrew Shvayka
*/
@Service
@Slf4j
public class BaseComponentDescriptorService implements ComponentDescriptorService {
@Autowired
private ComponentDescriptorDao componentDescriptorDao;
@Override
public ComponentDescriptor saveComponent(ComponentDescriptor component) {
componentValidator.validate(component);
Optional<ComponentDescriptorEntity> result = componentDescriptorDao.save(component);
if (result.isPresent()) {
return getData(result.get());
} else {
return getData(componentDescriptorDao.findByClazz(component.getClazz()));
}
}
@Override
public ComponentDescriptor findById(ComponentDescriptorId componentId) {
Validator.validateId(componentId, "Incorrect component id for search request.");
return getData(componentDescriptorDao.findById(componentId));
}
@Override
public ComponentDescriptor findByClazz(String clazz) {
Validator.validateString(clazz, "Incorrect clazz for search request.");
return getData(componentDescriptorDao.findByClazz(clazz));
}
@Override
public TextPageData<ComponentDescriptor> findByTypeAndPageLink(ComponentType type, TextPageLink pageLink) {
Validator.validatePageLink(pageLink, "Incorrect PageLink object for search plugin components request.");
List<ComponentDescriptorEntity> pluginEntities = componentDescriptorDao.findByTypeAndPageLink(type, pageLink);
List<ComponentDescriptor> components = convertDataList(pluginEntities);
return new TextPageData<>(components, pageLink);
}
@Override
public TextPageData<ComponentDescriptor> findByScopeAndTypeAndPageLink(ComponentScope scope, ComponentType type, TextPageLink pageLink) {
Validator.validatePageLink(pageLink, "Incorrect PageLink object for search plugin components request.");
List<ComponentDescriptorEntity> pluginEntities = componentDescriptorDao.findByScopeAndTypeAndPageLink(scope, type, pageLink);
List<ComponentDescriptor> components = convertDataList(pluginEntities);
return new TextPageData<>(components, pageLink);
}
@Override
public void deleteByClazz(String clazz) {
Validator.validateString(clazz, "Incorrect clazz for delete request.");
componentDescriptorDao.deleteByClazz(clazz);
}
@Override
public boolean validate(ComponentDescriptor component, JsonNode configuration) {
JsonValidator validator = JsonSchemaFactory.byDefault().getValidator();
try {
if (!component.getConfigurationDescriptor().has("schema")) {
throw new DataValidationException("Configuration descriptor doesn't contain schema property!");
}
JsonNode configurationSchema = component.getConfigurationDescriptor().get("schema");
ProcessingReport report = validator.validate(configurationSchema, configuration);
return report.isSuccess();
} catch (ProcessingException e) {
throw new IncorrectParameterException(e.getMessage(), e);
}
}
private DataValidator<ComponentDescriptor> componentValidator =
new DataValidator<ComponentDescriptor>() {
@Override
protected void validateDataImpl(ComponentDescriptor plugin) {
if (plugin.getType() == null) {
throw new DataValidationException("Component type should be specified!.");
}
if (plugin.getScope() == null) {
throw new DataValidationException("Component scope should be specified!.");
}
if (StringUtils.isEmpty(plugin.getName())) {
throw new DataValidationException("Component name should be specified!.");
}
if (StringUtils.isEmpty(plugin.getClazz())) {
throw new DataValidationException("Component clazz should be specified!.");
}
}
};
}

48
dao/src/main/java/org/thingsboard/server/dao/component/ComponentDescriptorDao.java

@ -0,0 +1,48 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.component;
import org.thingsboard.server.common.data.id.ComponentDescriptorId;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
import org.thingsboard.server.common.data.plugin.ComponentScope;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.dao.Dao;
import org.thingsboard.server.dao.model.ComponentDescriptorEntity;
import java.util.List;
import java.util.Optional;
/**
* @author Andrew Shvayka
*/
public interface ComponentDescriptorDao extends Dao<ComponentDescriptorEntity> {
Optional<ComponentDescriptorEntity> save(ComponentDescriptor component);
ComponentDescriptorEntity findById(ComponentDescriptorId componentId);
ComponentDescriptorEntity findByClazz(String clazz);
List<ComponentDescriptorEntity> findByTypeAndPageLink(ComponentType type, TextPageLink pageLink);
List<ComponentDescriptorEntity> findByScopeAndTypeAndPageLink(ComponentScope scope, ComponentType type, TextPageLink pageLink);
void deleteById(ComponentDescriptorId componentId);
void deleteByClazz(String clazz);
}

45
dao/src/main/java/org/thingsboard/server/dao/component/ComponentDescriptorService.java

@ -0,0 +1,45 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.component;
import com.fasterxml.jackson.databind.JsonNode;
import org.thingsboard.server.common.data.id.ComponentDescriptorId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
import org.thingsboard.server.common.data.plugin.ComponentScope;
import org.thingsboard.server.common.data.plugin.ComponentType;
/**
* @author Andrew Shvayka
*/
public interface ComponentDescriptorService {
ComponentDescriptor saveComponent(ComponentDescriptor component);
ComponentDescriptor findById(ComponentDescriptorId componentId);
ComponentDescriptor findByClazz(String clazz);
TextPageData<ComponentDescriptor> findByTypeAndPageLink(ComponentType type, TextPageLink pageLink);
TextPageData<ComponentDescriptor> findByScopeAndTypeAndPageLink(ComponentScope scope, ComponentType type, TextPageLink pageLink);
boolean validate(ComponentDescriptor component, JsonNode configuration);
void deleteByClazz(String clazz);
}

48
dao/src/main/java/org/thingsboard/server/dao/customer/CustomerDao.java

@ -0,0 +1,48 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.customer;
import java.util.List;
import java.util.UUID;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.dao.Dao;
import org.thingsboard.server.dao.model.CustomerEntity;
/**
* The Interface CustomerDao.
*/
public interface CustomerDao extends Dao<CustomerEntity> {
/**
* Save or update customer object
*
* @param customer the customer object
* @return saved customer object
*/
CustomerEntity save(Customer customer);
/**
* Find customers by tenant id and page link.
*
* @param tenantId the tenant id
* @param pageLink the page link
* @return the list of customer objects
*/
List<CustomerEntity> findCustomersByTenantId(UUID tenantId, TextPageLink pageLink);
}

63
dao/src/main/java/org/thingsboard/server/dao/customer/CustomerDaoImpl.java

@ -0,0 +1,63 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.customer;
import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.dao.AbstractSearchTextDao;
import org.thingsboard.server.dao.model.CustomerEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.thingsboard.server.dao.model.ModelConstants;
@Component
@Slf4j
public class CustomerDaoImpl extends AbstractSearchTextDao<CustomerEntity> implements CustomerDao {
@Override
protected Class<CustomerEntity> getColumnFamilyClass() {
return CustomerEntity.class;
}
@Override
protected String getColumnFamilyName() {
return ModelConstants.CUSTOMER_COLUMN_FAMILY_NAME;
}
@Override
public CustomerEntity save(Customer customer) {
log.debug("Save customer [{}] ", customer);
return save(new CustomerEntity(customer));
}
@Override
public List<CustomerEntity> findCustomersByTenantId(UUID tenantId, TextPageLink pageLink) {
log.debug("Try to find customers by tenantId [{}] and pageLink [{}]", tenantId, pageLink);
List<CustomerEntity> customerEntities = findPageWithTextSearch(ModelConstants.CUSTOMER_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
Arrays.asList(eq(ModelConstants.CUSTOMER_TENANT_ID_PROPERTY, tenantId)),
pageLink);
log.trace("Found customers [{}] by tenantId [{}] and pageLink [{}]", customerEntities, tenantId, pageLink);
return customerEntities;
}
}

36
dao/src/main/java/org/thingsboard/server/dao/customer/CustomerService.java

@ -0,0 +1,36 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.customer;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
public interface CustomerService {
public Customer findCustomerById(CustomerId customerId);
public Customer saveCustomer(Customer customer);
public void deleteCustomer(CustomerId customerId);
public TextPageData<Customer> findCustomersByTenantId(TenantId tenantId, TextPageLink pageLink);
public void deleteCustomersByTenantId(TenantId tenantId);
}

145
dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java

@ -0,0 +1,145 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.customer;
import static org.thingsboard.server.dao.DaoUtil.convertDataList;
import static org.thingsboard.server.dao.DaoUtil.getData;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.model.CustomerEntity;
import org.thingsboard.server.dao.model.TenantEntity;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.PaginatedRemover;
import org.thingsboard.server.dao.tenant.TenantDao;
import org.thingsboard.server.dao.user.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.thingsboard.server.dao.service.Validator;
@Service
@Slf4j
public class CustomerServiceImpl implements CustomerService {
@Autowired
private CustomerDao customerDao;
@Autowired
private UserService userService;
@Autowired
private TenantDao tenantDao;
@Autowired
private DeviceService deviceService;
@Autowired
private DashboardService dashboardService;
@Override
public Customer findCustomerById(CustomerId customerId) {
log.trace("Executing findCustomerById [{}]", customerId);
Validator.validateId(customerId, "Incorrect customerId " + customerId);
CustomerEntity customerEntity = customerDao.findById(customerId.getId());
return getData(customerEntity);
}
@Override
public Customer saveCustomer(Customer customer) {
log.trace("Executing saveCustomer [{}]", customer);
customerValidator.validate(customer);
CustomerEntity customerEntity = customerDao.save(customer);
return getData(customerEntity);
}
@Override
public void deleteCustomer(CustomerId customerId) {
log.trace("Executing deleteCustomer [{}]", customerId);
Validator.validateId(customerId, "Incorrect tenantId " + customerId);
Customer customer = findCustomerById(customerId);
if (customer == null) {
throw new IncorrectParameterException("Unable to delete non-existent customer.");
}
dashboardService.unassignCustomerDashboards(customer.getTenantId(), customerId);
deviceService.unassignCustomerDevices(customer.getTenantId(), customerId);
userService.deleteCustomerUsers(customer.getTenantId(), customerId);
customerDao.removeById(customerId.getId());
}
@Override
public TextPageData<Customer> findCustomersByTenantId(TenantId tenantId, TextPageLink pageLink) {
log.trace("Executing findCustomersByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink);
Validator.validateId(tenantId, "Incorrect tenantId " + tenantId);
Validator.validatePageLink(pageLink, "Incorrect page link " + pageLink);
List<CustomerEntity> customerEntities = customerDao.findCustomersByTenantId(tenantId.getId(), pageLink);
List<Customer> customers = convertDataList(customerEntities);
return new TextPageData<Customer>(customers, pageLink);
}
@Override
public void deleteCustomersByTenantId(TenantId tenantId) {
log.trace("Executing deleteCustomersByTenantId, tenantId [{}]", tenantId);
Validator.validateId(tenantId, "Incorrect tenantId " + tenantId);
customersByTenantRemover.removeEntitites(tenantId);
}
private DataValidator<Customer> customerValidator =
new DataValidator<Customer>() {
@Override
protected void validateDataImpl(Customer customer) {
if (StringUtils.isEmpty(customer.getTitle())) {
throw new DataValidationException("Customer title should be specified!");
}
if (!StringUtils.isEmpty(customer.getEmail())) {
validateEmail(customer.getEmail());
}
if (customer.getTenantId() == null) {
throw new DataValidationException("Customer should be assigned to tenant!");
} else {
TenantEntity tenant = tenantDao.findById(customer.getTenantId().getId());
if (tenant == null) {
throw new DataValidationException("Customer is referencing to non-existent tenant!");
}
}
}
};
private PaginatedRemover<TenantId, CustomerEntity> customersByTenantRemover =
new PaginatedRemover<TenantId, CustomerEntity>() {
@Override
protected List<CustomerEntity> findEntities(TenantId id, TextPageLink pageLink) {
return customerDao.findCustomersByTenantId(id.getId(), pageLink);
}
@Override
protected void removeEntity(CustomerEntity entity) {
deleteCustomer(new CustomerId(entity.getId()));
}
};
}

60
dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDao.java

@ -0,0 +1,60 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.dashboard;
import java.util.List;
import java.util.UUID;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.dao.Dao;
import org.thingsboard.server.dao.model.DashboardEntity;
/**
* The Interface DashboardDao.
*
* @param <T> the generic type
*/
public interface DashboardDao extends Dao<DashboardEntity> {
/**
* Save or update dashboard object
*
* @param dashboard the dashboard object
* @return saved dashboard object
*/
DashboardEntity save(Dashboard dashboard);
/**
* Find dashboards by tenantId and page link.
*
* @param tenantId the tenantId
* @param pageLink the page link
* @return the list of dashboard objects
*/
List<DashboardEntity> findDashboardsByTenantId(UUID tenantId, TextPageLink pageLink);
/**
* Find dashboards by tenantId, customerId and page link.
*
* @param tenantId the tenantId
* @param customerId the customerId
* @param pageLink the page link
* @return the list of dashboard objects
*/
List<DashboardEntity> findDashboardsByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink pageLink);
}

83
dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDaoImpl.java

@ -0,0 +1,83 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.dashboard;
import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
import static org.thingsboard.server.dao.model.ModelConstants.DASHBOARD_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME;
import static org.thingsboard.server.dao.model.ModelConstants.DASHBOARD_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME;
import static org.thingsboard.server.dao.model.ModelConstants.DASHBOARD_COLUMN_FAMILY_NAME;
import static org.thingsboard.server.dao.model.ModelConstants.DASHBOARD_CUSTOMER_ID_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.DASHBOARD_TENANT_ID_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.dao.AbstractSearchTextDao;
import org.thingsboard.server.dao.model.DashboardEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Component
@Slf4j
public class DashboardDaoImpl extends AbstractSearchTextDao<DashboardEntity> implements DashboardDao {
@Override
protected Class<DashboardEntity> getColumnFamilyClass() {
return DashboardEntity.class;
}
@Override
protected String getColumnFamilyName() {
return DASHBOARD_COLUMN_FAMILY_NAME;
}
@Override
public DashboardEntity save(Dashboard dashboard) {
log.debug("Save dashboard [{}] ", dashboard);
return save(new DashboardEntity(dashboard));
}
@Override
public List<DashboardEntity> findDashboardsByTenantId(UUID tenantId, TextPageLink pageLink) {
log.debug("Try to find dashboards by tenantId [{}] and pageLink [{}]", tenantId, pageLink);
List<DashboardEntity> dashboardEntities = findPageWithTextSearch(DASHBOARD_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
Arrays.asList(eq(DASHBOARD_TENANT_ID_PROPERTY, tenantId),
eq(DASHBOARD_CUSTOMER_ID_PROPERTY, NULL_UUID)),
pageLink);
log.trace("Found dashboards [{}] by tenantId [{}] and pageLink [{}]", dashboardEntities, tenantId, pageLink);
return dashboardEntities;
}
@Override
public List<DashboardEntity> findDashboardsByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink pageLink) {
log.debug("Try to find dashboards by tenantId [{}], customerId[{}] and pageLink [{}]", tenantId, customerId, pageLink);
List<DashboardEntity> dashboardEntities = findPageWithTextSearch(DASHBOARD_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
Arrays.asList(eq(DASHBOARD_CUSTOMER_ID_PROPERTY, customerId),
eq(DASHBOARD_TENANT_ID_PROPERTY, tenantId)),
pageLink);
log.trace("Found dashboards [{}] by tenantId [{}], customerId [{}] and pageLink [{}]", dashboardEntities, tenantId, customerId, pageLink);
return dashboardEntities;
}
}

45
dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java

@ -0,0 +1,45 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.dashboard;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
public interface DashboardService {
public Dashboard findDashboardById(DashboardId dashboardId);
public Dashboard saveDashboard(Dashboard dashboard);
public Dashboard assignDashboardToCustomer(DashboardId dashboardId, CustomerId customerId);
public Dashboard unassignDashboardFromCustomer(DashboardId dashboardId);
public void deleteDashboard(DashboardId dashboardId);
public TextPageData<Dashboard> findDashboardsByTenantId(TenantId tenantId, TextPageLink pageLink);
public void deleteDashboardsByTenantId(TenantId tenantId);
public TextPageData<Dashboard> findDashboardsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TextPageLink pageLink);
public void unassignCustomerDashboards(TenantId tenantId, CustomerId customerId);
}

195
dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java

@ -0,0 +1,195 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.dashboard;
import static org.thingsboard.server.dao.DaoUtil.convertDataList;
import static org.thingsboard.server.dao.DaoUtil.getData;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.dao.customer.CustomerDao;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.model.CustomerEntity;
import org.thingsboard.server.dao.model.DashboardEntity;
import org.thingsboard.server.dao.model.TenantEntity;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.PaginatedRemover;
import org.thingsboard.server.dao.tenant.TenantDao;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.service.Validator;
@Service
@Slf4j
public class DashboardServiceImpl implements DashboardService {
@Autowired
private DashboardDao dashboardDao;
@Autowired
private TenantDao tenantDao;
@Autowired
private CustomerDao customerDao;
@Override
public Dashboard findDashboardById(DashboardId dashboardId) {
log.trace("Executing findDashboardById [{}]", dashboardId);
Validator.validateId(dashboardId, "Incorrect dashboardId " + dashboardId);
DashboardEntity dashboardEntity = dashboardDao.findById(dashboardId.getId());
return getData(dashboardEntity);
}
@Override
public Dashboard saveDashboard(Dashboard dashboard) {
log.trace("Executing saveDashboard [{}]", dashboard);
dashboardValidator.validate(dashboard);
DashboardEntity dashboardEntity = dashboardDao.save(dashboard);
return getData(dashboardEntity);
}
@Override
public Dashboard assignDashboardToCustomer(DashboardId dashboardId, CustomerId customerId) {
Dashboard dashboard = findDashboardById(dashboardId);
dashboard.setCustomerId(customerId);
return saveDashboard(dashboard);
}
@Override
public Dashboard unassignDashboardFromCustomer(DashboardId dashboardId) {
Dashboard dashboard = findDashboardById(dashboardId);
dashboard.setCustomerId(null);
return saveDashboard(dashboard);
}
@Override
public void deleteDashboard(DashboardId dashboardId) {
log.trace("Executing deleteDashboard [{}]", dashboardId);
Validator.validateId(dashboardId, "Incorrect dashboardId " + dashboardId);
dashboardDao.removeById(dashboardId.getId());
}
@Override
public TextPageData<Dashboard> findDashboardsByTenantId(TenantId tenantId, TextPageLink pageLink) {
log.trace("Executing findDashboardsByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink);
Validator.validateId(tenantId, "Incorrect tenantId " + tenantId);
Validator.validatePageLink(pageLink, "Incorrect page link " + pageLink);
List<DashboardEntity> dashboardEntities = dashboardDao.findDashboardsByTenantId(tenantId.getId(), pageLink);
List<Dashboard> dashboards = convertDataList(dashboardEntities);
return new TextPageData<Dashboard>(dashboards, pageLink);
}
@Override
public void deleteDashboardsByTenantId(TenantId tenantId) {
log.trace("Executing deleteDashboardsByTenantId, tenantId [{}]", tenantId);
Validator.validateId(tenantId, "Incorrect tenantId " + tenantId);
tenantDashboardsRemover.removeEntitites(tenantId);
}
@Override
public TextPageData<Dashboard> findDashboardsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TextPageLink pageLink) {
log.trace("Executing findDashboardsByTenantIdAndCustomerId, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink);
Validator.validateId(tenantId, "Incorrect tenantId " + tenantId);
Validator.validateId(customerId, "Incorrect customerId " + customerId);
Validator.validatePageLink(pageLink, "Incorrect page link " + pageLink);
List<DashboardEntity> dashboardEntities = dashboardDao.findDashboardsByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink);
List<Dashboard> dashboards = convertDataList(dashboardEntities);
return new TextPageData<Dashboard>(dashboards, pageLink);
}
@Override
public void unassignCustomerDashboards(TenantId tenantId, CustomerId customerId) {
log.trace("Executing unassignCustomerDashboards, tenantId [{}], customerId [{}]", tenantId, customerId);
Validator.validateId(tenantId, "Incorrect tenantId " + tenantId);
Validator.validateId(customerId, "Incorrect customerId " + customerId);
new CustomerDashboardsUnassigner(tenantId).removeEntitites(customerId);
}
private DataValidator<Dashboard> dashboardValidator =
new DataValidator<Dashboard>() {
@Override
protected void validateDataImpl(Dashboard dashboard) {
if (StringUtils.isEmpty(dashboard.getTitle())) {
throw new DataValidationException("Dashboard title should be specified!");
}
if (dashboard.getTenantId() == null) {
throw new DataValidationException("Dashboard should be assigned to tenant!");
} else {
TenantEntity tenant = tenantDao.findById(dashboard.getTenantId().getId());
if (tenant == null) {
throw new DataValidationException("Dashboard is referencing to non-existent tenant!");
}
}
if (dashboard.getCustomerId() == null) {
dashboard.setCustomerId(new CustomerId(ModelConstants.NULL_UUID));
} else if (!dashboard.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) {
CustomerEntity customer = customerDao.findById(dashboard.getCustomerId().getId());
if (customer == null) {
throw new DataValidationException("Can't assign dashboard to non-existent customer!");
}
if (!customer.getTenantId().equals(dashboard.getTenantId().getId())) {
throw new DataValidationException("Can't assign dashboard to customer from different tenant!");
}
}
}
};
private PaginatedRemover<TenantId, DashboardEntity> tenantDashboardsRemover =
new PaginatedRemover<TenantId, DashboardEntity>() {
@Override
protected List<DashboardEntity> findEntities(TenantId id, TextPageLink pageLink) {
return dashboardDao.findDashboardsByTenantId(id.getId(), pageLink);
}
@Override
protected void removeEntity(DashboardEntity entity) {
deleteDashboard(new DashboardId(entity.getId()));
}
};
class CustomerDashboardsUnassigner extends PaginatedRemover<CustomerId, DashboardEntity> {
private TenantId tenantId;
CustomerDashboardsUnassigner(TenantId tenantId) {
this.tenantId = tenantId;
}
@Override
protected List<DashboardEntity> findEntities(CustomerId id, TextPageLink pageLink) {
return dashboardDao.findDashboardsByTenantIdAndCustomerId(tenantId.getId(), id.getId(), pageLink);
}
@Override
protected void removeEntity(DashboardEntity entity) {
unassignDashboardFromCustomer(new DashboardId(entity.getId()));
}
}
}

55
dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsDao.java

@ -0,0 +1,55 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.device;
import java.util.UUID;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.dao.Dao;
import org.thingsboard.server.dao.model.DeviceCredentialsEntity;
/**
* The Interface DeviceCredentialsDao.
*
* @param <T> the generic type
*/
public interface DeviceCredentialsDao extends Dao<DeviceCredentialsEntity> {
/**
* Save or update device credentials object
*
* @param deviceCredentials the device credentials object
* @return saved device credentials object
*/
DeviceCredentialsEntity save(DeviceCredentials deviceCredentials);
/**
* Find device credentials by device id.
*
* @param deviceId the device id
* @return the device credentials object
*/
DeviceCredentialsEntity findByDeviceId(UUID deviceId);
/**
* Find device credentials by credentials id.
*
* @param credentialsId the credentials id
* @return the device credentials object
*/
DeviceCredentialsEntity findByCredentialsId(String credentialsId);
}

77
dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsDaoImpl.java

@ -0,0 +1,77 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.device;
import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
import static com.datastax.driver.core.querybuilder.QueryBuilder.select;
import java.util.UUID;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.dao.AbstractModelDao;
import org.thingsboard.server.dao.model.DeviceCredentialsEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Repository;
import com.datastax.driver.core.querybuilder.Select.Where;
import org.thingsboard.server.dao.model.ModelConstants;
@Component
@Slf4j
public class DeviceCredentialsDaoImpl extends AbstractModelDao<DeviceCredentialsEntity> implements DeviceCredentialsDao {
@Override
protected Class<DeviceCredentialsEntity> getColumnFamilyClass() {
return DeviceCredentialsEntity.class;
}
@Override
protected String getColumnFamilyName() {
return ModelConstants.DEVICE_CREDENTIALS_COLUMN_FAMILY_NAME;
}
@Override
public DeviceCredentialsEntity findByDeviceId(UUID deviceId) {
log.debug("Try to find device credentials by deviceId [{}] ", deviceId);
Where query = select().from(ModelConstants.DEVICE_CREDENTIALS_BY_DEVICE_COLUMN_FAMILY_NAME)
.where(eq(ModelConstants.DEVICE_CREDENTIALS_DEVICE_ID_PROPERTY, deviceId));
log.trace("Execute query {}", query);
DeviceCredentialsEntity deviceCredentialsEntity = findOneByStatement(query);
log.trace("Found device credentials [{}] by deviceId [{}]", deviceCredentialsEntity, deviceId);
return deviceCredentialsEntity;
}
@Override
public DeviceCredentialsEntity findByCredentialsId(String credentialsId) {
log.debug("Try to find device credentials by credentialsId [{}] ", credentialsId);
Where query = select().from(ModelConstants.DEVICE_CREDENTIALS_BY_CREDENTIALS_ID_COLUMN_FAMILY_NAME)
.where(eq(ModelConstants.DEVICE_CREDENTIALS_CREDENTIALS_ID_PROPERTY, credentialsId));
log.trace("Execute query {}", query);
DeviceCredentialsEntity deviceCredentialsEntity = findOneByStatement(query);
log.trace("Found device credentials [{}] by credentialsId [{}]", deviceCredentialsEntity, credentialsId);
return deviceCredentialsEntity;
}
@Override
public DeviceCredentialsEntity save(DeviceCredentials deviceCredentials) {
log.debug("Save device credentials [{}] ", deviceCredentials);
return save(new DeviceCredentialsEntity(deviceCredentials));
}
}

39
dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsService.java

@ -0,0 +1,39 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.device;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import static org.thingsboard.server.common.data.CacheConstants.DEVICE_CREDENTIALS_CACHE;
public interface DeviceCredentialsService {
DeviceCredentials findDeviceCredentialsByDeviceId(DeviceId deviceId);
@Cacheable(cacheNames = DEVICE_CREDENTIALS_CACHE, unless="#result == null")
DeviceCredentials findDeviceCredentialsByCredentialsId(String credentialsId);
@CacheEvict(cacheNames = DEVICE_CREDENTIALS_CACHE, keyGenerator="previousDeviceCredentialsId", beforeInvocation = true)
DeviceCredentials updateDeviceCredentials(DeviceCredentials deviceCredentials);
DeviceCredentials createDeviceCredentials(DeviceCredentials deviceCredentials);
@CacheEvict(cacheNames = DEVICE_CREDENTIALS_CACHE, key="#deviceCredentials.credentialsId")
void deleteDeviceCredentials(DeviceCredentials deviceCredentials);
}

134
dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java

@ -0,0 +1,134 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.device;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.model.DeviceCredentialsEntity;
import org.thingsboard.server.dao.service.DataValidator;
import java.util.Optional;
import static org.thingsboard.server.dao.DaoUtil.getData;
import static org.thingsboard.server.dao.service.Validator.validateId;
import static org.thingsboard.server.dao.service.Validator.validateString;
@Service
@Slf4j
public class DeviceCredentialsServiceImpl implements DeviceCredentialsService {
@Autowired
private DeviceCredentialsDao deviceCredentialsDao;
@Autowired
private DeviceService deviceService;
@Override
public DeviceCredentials findDeviceCredentialsByDeviceId(DeviceId deviceId) {
log.trace("Executing findDeviceCredentialsByDeviceId [{}]", deviceId);
validateId(deviceId, "Incorrect deviceId " + deviceId);
DeviceCredentialsEntity deviceCredentialsEntity = deviceCredentialsDao.findByDeviceId(deviceId.getId());
return getData(deviceCredentialsEntity);
}
@Override
public DeviceCredentials findDeviceCredentialsByCredentialsId(String credentialsId) {
log.trace("Executing findDeviceCredentialsByCredentialsId [{}]", credentialsId);
validateString(credentialsId, "Incorrect credentialsId " + credentialsId);
DeviceCredentialsEntity deviceCredentialsEntity = deviceCredentialsDao.findByCredentialsId(credentialsId);
return getData(deviceCredentialsEntity);
}
@Override
public DeviceCredentials updateDeviceCredentials(DeviceCredentials deviceCredentials) {
return saveOrUpdare(deviceCredentials);
}
@Override
public DeviceCredentials createDeviceCredentials(DeviceCredentials deviceCredentials) {
return saveOrUpdare(deviceCredentials);
}
private DeviceCredentials saveOrUpdare(DeviceCredentials deviceCredentials) {
log.trace("Executing updateDeviceCredentials [{}]", deviceCredentials);
credentialsValidator.validate(deviceCredentials);
return getData(deviceCredentialsDao.save(deviceCredentials));
}
@Override
public void deleteDeviceCredentials(DeviceCredentials deviceCredentials) {
log.trace("Executing deleteDeviceCredentials [{}]", deviceCredentials);
deviceCredentialsDao.removeById(deviceCredentials.getUuidId());
}
private DataValidator<DeviceCredentials> credentialsValidator =
new DataValidator<DeviceCredentials>() {
@Override
protected void validateCreate(DeviceCredentials deviceCredentials) {
DeviceCredentialsEntity existingCredentialsEntity = deviceCredentialsDao.findByCredentialsId(deviceCredentials.getCredentialsId());
if (existingCredentialsEntity != null) {
throw new DataValidationException("Create of existent device credentials!");
}
}
@Override
protected void validateUpdate(DeviceCredentials deviceCredentials) {
DeviceCredentialsEntity existingCredentialsEntity = deviceCredentialsDao.findById(deviceCredentials.getUuidId());
if (existingCredentialsEntity == null) {
throw new DataValidationException("Unable to update non-existent device credentials!");
}
DeviceCredentialsEntity sameCredentialsIdEntity = deviceCredentialsDao.findByCredentialsId(deviceCredentials.getCredentialsId());
if (sameCredentialsIdEntity != null && !sameCredentialsIdEntity.getId().equals(deviceCredentials.getUuidId())) {
throw new DataValidationException("Specified credentials are already registered!");
}
}
@Override
protected void validateDataImpl(DeviceCredentials deviceCredentials) {
if (deviceCredentials.getDeviceId() == null) {
throw new DataValidationException("Device credentials should be assigned to device!");
}
if (deviceCredentials.getCredentialsType() == null) {
throw new DataValidationException("Device credentials type should be specified!");
}
if (StringUtils.isEmpty(deviceCredentials.getCredentialsId())) {
throw new DataValidationException("Device credentials id should be specified!");
}
switch (deviceCredentials.getCredentialsType()) {
case ACCESS_TOKEN:
if (deviceCredentials.getCredentialsId().length() < 1 || deviceCredentials.getCredentialsId().length() > 20) {
throw new DataValidationException("Incorrect access token length [" + deviceCredentials.getCredentialsId().length() + "]!");
}
break;
default:
break;
}
Device device = deviceService.findDeviceById(deviceCredentials.getDeviceId());
if (device == null) {
throw new DataValidationException("Can't assign device credentials to non-existent device!");
}
}
};
}

68
dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java

@ -0,0 +1,68 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.device;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.dao.Dao;
import org.thingsboard.server.dao.model.DeviceEntity;
/**
* The Interface DeviceDao.
*
*/
public interface DeviceDao extends Dao<DeviceEntity> {
/**
* Save or update device object
*
* @param device the device object
* @return saved device object
*/
DeviceEntity save(Device device);
/**
* Find devices by tenantId and page link.
*
* @param tenantId the tenantId
* @param pageLink the page link
* @return the list of device objects
*/
List<DeviceEntity> findDevicesByTenantId(UUID tenantId, TextPageLink pageLink);
/**
* Find devices by tenantId, customerId and page link.
*
* @param tenantId the tenantId
* @param customerId the customerId
* @param pageLink the page link
* @return the list of device objects
*/
List<DeviceEntity> findDevicesByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink pageLink);
/**
* Find devices by tenantId and device name.
*
* @param tenantId the tenantId
* @param name the device name
* @return the optional device object
*/
Optional<DeviceEntity> findDevicesByTenantIdAndName(UUID tenantId, String name);
}

85
dao/src/main/java/org/thingsboard/server/dao/device/DeviceDaoImpl.java

@ -0,0 +1,85 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.device;
import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
import static com.datastax.driver.core.querybuilder.QueryBuilder.select;
import static org.thingsboard.server.dao.model.ModelConstants.*;
import java.util.*;
import com.datastax.driver.core.querybuilder.Select;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.dao.AbstractSearchTextDao;
import org.thingsboard.server.dao.model.DeviceEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Component
@Slf4j
public class DeviceDaoImpl extends AbstractSearchTextDao<DeviceEntity> implements DeviceDao {
@Override
protected Class<DeviceEntity> getColumnFamilyClass() {
return DeviceEntity.class;
}
@Override
protected String getColumnFamilyName() {
return DEVICE_COLUMN_FAMILY_NAME;
}
@Override
public DeviceEntity save(Device device) {
log.debug("Save device [{}] ", device);
return save(new DeviceEntity(device));
}
@Override
public List<DeviceEntity> findDevicesByTenantId(UUID tenantId, TextPageLink pageLink) {
log.debug("Try to find devices by tenantId [{}] and pageLink [{}]", tenantId, pageLink);
List<DeviceEntity> deviceEntities = findPageWithTextSearch(DEVICE_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
Collections.singletonList(eq(DEVICE_TENANT_ID_PROPERTY, tenantId)), pageLink);
log.trace("Found devices [{}] by tenantId [{}] and pageLink [{}]", deviceEntities, tenantId, pageLink);
return deviceEntities;
}
@Override
public List<DeviceEntity> findDevicesByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink pageLink) {
log.debug("Try to find devices by tenantId [{}], customerId[{}] and pageLink [{}]", tenantId, customerId, pageLink);
List<DeviceEntity> deviceEntities = findPageWithTextSearch(DEVICE_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
Arrays.asList(eq(DEVICE_CUSTOMER_ID_PROPERTY, customerId),
eq(DEVICE_TENANT_ID_PROPERTY, tenantId)),
pageLink);
log.trace("Found devices [{}] by tenantId [{}], customerId [{}] and pageLink [{}]", deviceEntities, tenantId, customerId, pageLink);
return deviceEntities;
}
@Override
public Optional<DeviceEntity> findDevicesByTenantIdAndName(UUID tenantId, String deviceName) {
Select select = select().from(DEVICE_BY_TENANT_AND_NAME_VIEW_NAME);
Select.Where query = select.where();
query.and(eq(DEVICE_TENANT_ID_PROPERTY, tenantId));
query.and(eq(DEVICE_NAME_PROPERTY, deviceName));
return Optional.ofNullable(findOneByStatement(query));
}
}

44
dao/src/main/java/org/thingsboard/server/dao/device/DeviceService.java

@ -0,0 +1,44 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.device;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
public interface DeviceService {
Device findDeviceById(DeviceId deviceId);
Device saveDevice(Device device);
Device assignDeviceToCustomer(DeviceId deviceId, CustomerId customerId);
Device unassignDeviceFromCustomer(DeviceId deviceId);
void deleteDevice(DeviceId deviceId);
TextPageData<Device> findDevicesByTenantId(TenantId tenantId, TextPageLink pageLink);
void deleteDevicesByTenantId(TenantId tenantId);
TextPageData<Device> findDevicesByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TextPageLink pageLink);
void unassignCustomerDevices(TenantId tenantId, CustomerId customerId);
}

231
dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java

@ -0,0 +1,231 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.device;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
import org.thingsboard.server.dao.customer.CustomerDao;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.model.CustomerEntity;
import org.thingsboard.server.dao.model.DeviceEntity;
import org.thingsboard.server.dao.model.TenantEntity;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.PaginatedRemover;
import org.thingsboard.server.dao.tenant.TenantDao;
import java.util.List;
import static org.thingsboard.server.dao.DaoUtil.convertDataList;
import static org.thingsboard.server.dao.DaoUtil.getData;
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
import static org.thingsboard.server.dao.service.Validator.validateId;
import static org.thingsboard.server.dao.service.Validator.validatePageLink;
@Service
@Slf4j
public class DeviceServiceImpl implements DeviceService {
@Autowired
private DeviceDao deviceDao;
@Autowired
private TenantDao tenantDao;
@Autowired
private CustomerDao customerDao;
@Autowired
private DeviceCredentialsService deviceCredentialsService;
@Override
public Device findDeviceById(DeviceId deviceId) {
log.trace("Executing findDeviceById [{}]", deviceId);
validateId(deviceId, "Incorrect deviceId " + deviceId);
DeviceEntity deviceEntity = deviceDao.findById(deviceId.getId());
return getData(deviceEntity);
}
@Override
public Device saveDevice(Device device) {
log.trace("Executing saveDevice [{}]", device);
deviceValidator.validate(device);
DeviceEntity deviceEntity = deviceDao.save(device);
if (device.getId() == null) {
DeviceCredentials deviceCredentials = new DeviceCredentials();
deviceCredentials.setDeviceId(new DeviceId(deviceEntity.getId()));
deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN);
deviceCredentials.setCredentialsId(RandomStringUtils.randomAlphanumeric(20));
deviceCredentialsService.createDeviceCredentials(deviceCredentials);
}
return getData(deviceEntity);
}
@Override
public Device assignDeviceToCustomer(DeviceId deviceId, CustomerId customerId) {
Device device = findDeviceById(deviceId);
device.setCustomerId(customerId);
return saveDevice(device);
}
@Override
public Device unassignDeviceFromCustomer(DeviceId deviceId) {
Device device = findDeviceById(deviceId);
device.setCustomerId(null);
return saveDevice(device);
}
@Override
public void deleteDevice(DeviceId deviceId) {
log.trace("Executing deleteDevice [{}]", deviceId);
validateId(deviceId, "Incorrect deviceId " + deviceId);
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(deviceId);
if (deviceCredentials != null) {
deviceCredentialsService.deleteDeviceCredentials(deviceCredentials);
}
deviceDao.removeById(deviceId.getId());
}
@Override
public TextPageData<Device> findDevicesByTenantId(TenantId tenantId, TextPageLink pageLink) {
log.trace("Executing findDevicesByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink);
validateId(tenantId, "Incorrect tenantId " + tenantId);
validatePageLink(pageLink, "Incorrect page link " + pageLink);
List<DeviceEntity> deviceEntities = deviceDao.findDevicesByTenantId(tenantId.getId(), pageLink);
List<Device> devices = convertDataList(deviceEntities);
return new TextPageData<Device>(devices, pageLink);
}
@Override
public void deleteDevicesByTenantId(TenantId tenantId) {
log.trace("Executing deleteDevicesByTenantId, tenantId [{}]", tenantId);
validateId(tenantId, "Incorrect tenantId " + tenantId);
tenantDevicesRemover.removeEntitites(tenantId);
}
@Override
public TextPageData<Device> findDevicesByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TextPageLink pageLink) {
log.trace("Executing findDevicesByTenantIdAndCustomerId, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink);
validateId(tenantId, "Incorrect tenantId " + tenantId);
validateId(customerId, "Incorrect customerId " + customerId);
validatePageLink(pageLink, "Incorrect page link " + pageLink);
List<DeviceEntity> deviceEntities = deviceDao.findDevicesByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink);
List<Device> devices = convertDataList(deviceEntities);
return new TextPageData<Device>(devices, pageLink);
}
@Override
public void unassignCustomerDevices(TenantId tenantId, CustomerId customerId) {
log.trace("Executing unassignCustomerDevices, tenantId [{}], customerId [{}]", tenantId, customerId);
validateId(tenantId, "Incorrect tenantId " + tenantId);
validateId(customerId, "Incorrect customerId " + customerId);
new CustomerDevicesUnassigner(tenantId).removeEntitites(customerId);
}
private DataValidator<Device> deviceValidator =
new DataValidator<Device>() {
@Override
protected void validateCreate(Device device) {
deviceDao.findDevicesByTenantIdAndName(device.getTenantId().getId(), device.getName()).ifPresent(
d -> {
throw new DataValidationException("Device with such name already exists!");
}
);
}
@Override
protected void validateUpdate(Device device) {
deviceDao.findDevicesByTenantIdAndName(device.getTenantId().getId(), device.getName()).ifPresent(
d -> {
if (!d.getId().equals(device.getUuidId())) {
throw new DataValidationException("Device with such name already exists!");
}
}
);
}
@Override
protected void validateDataImpl(Device device) {
if (StringUtils.isEmpty(device.getName())) {
throw new DataValidationException("Device name should be specified!");
}
if (device.getTenantId() == null) {
throw new DataValidationException("Device should be assigned to tenant!");
} else {
TenantEntity tenant = tenantDao.findById(device.getTenantId().getId());
if (tenant == null) {
throw new DataValidationException("Device is referencing to non-existent tenant!");
}
}
if (device.getCustomerId() == null) {
device.setCustomerId(new CustomerId(NULL_UUID));
} else if (!device.getCustomerId().getId().equals(NULL_UUID)) {
CustomerEntity customer = customerDao.findById(device.getCustomerId().getId());
if (customer == null) {
throw new DataValidationException("Can't assign device to non-existent customer!");
}
if (!customer.getTenantId().equals(device.getTenantId().getId())) {
throw new DataValidationException("Can't assign device to customer from different tenant!");
}
}
}
};
private PaginatedRemover<TenantId, DeviceEntity> tenantDevicesRemover =
new PaginatedRemover<TenantId, DeviceEntity>() {
@Override
protected List<DeviceEntity> findEntities(TenantId id, TextPageLink pageLink) {
return deviceDao.findDevicesByTenantId(id.getId(), pageLink);
}
@Override
protected void removeEntity(DeviceEntity entity) {
deleteDevice(new DeviceId(entity.getId()));
}
};
class CustomerDevicesUnassigner extends PaginatedRemover<CustomerId, DeviceEntity> {
private TenantId tenantId;
CustomerDevicesUnassigner(TenantId tenantId) {
this.tenantId = tenantId;
}
@Override
protected List<DeviceEntity> findEntities(CustomerId id, TextPageLink pageLink) {
return deviceDao.findDevicesByTenantIdAndCustomerId(tenantId.getId(), id.getId(), pageLink);
}
@Override
protected void removeEntity(DeviceEntity entity) {
unassignDeviceFromCustomer(new DeviceId(entity.getId()));
}
}
}

136
dao/src/main/java/org/thingsboard/server/dao/event/BaseEventDao.java

@ -0,0 +1,136 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.event;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.querybuilder.Insert;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Select;
import com.datastax.driver.core.utils.UUIDs;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.Event;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.dao.AbstractSearchTimeDao;
import org.thingsboard.server.dao.model.EventEntity;
import org.thingsboard.server.dao.model.ModelConstants;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
import static com.datastax.driver.core.querybuilder.QueryBuilder.select;
import static org.thingsboard.server.dao.model.ModelConstants.EVENT_BY_ID_VIEW_NAME;
import static org.thingsboard.server.dao.model.ModelConstants.EVENT_BY_TYPE_AND_ID_VIEW_NAME;
import static org.thingsboard.server.dao.model.ModelConstants.EVENT_COLUMN_FAMILY_NAME;
@Component
@Slf4j
public class BaseEventDao extends AbstractSearchTimeDao<EventEntity> implements EventDao {
@Override
protected Class<EventEntity> getColumnFamilyClass() {
return EventEntity.class;
}
@Override
protected String getColumnFamilyName() {
return EVENT_COLUMN_FAMILY_NAME;
}
@Override
public EventEntity save(Event event) {
log.debug("Save event [{}] ", event);
return save(new EventEntity(event), false).orElse(null);
}
@Override
public Optional<EventEntity> saveIfNotExists(Event event) {
return save(new EventEntity(event), true);
}
@Override
public EventEntity findEvent(UUID tenantId, EntityId entityId, String eventType, String eventUid) {
log.debug("Search event entity by [{}][{}][{}][{}]", tenantId, entityId, eventType, eventUid);
Select.Where query = select().from(getColumnFamilyName()).where(
eq(ModelConstants.EVENT_TENANT_ID_PROPERTY, tenantId))
.and(eq(ModelConstants.EVENT_ENTITY_TYPE_PROPERTY, entityId.getEntityType()))
.and(eq(ModelConstants.EVENT_ENTITY_ID_PROPERTY, entityId.getId()))
.and(eq(ModelConstants.EVENT_TYPE_PROPERTY, eventType))
.and(eq(ModelConstants.EVENT_UID_PROPERTY, eventUid));
log.trace("Execute query [{}]", query);
EventEntity entity = findOneByStatement(query);
if (log.isTraceEnabled()) {
log.trace("Search result: [{}] for event entity [{}]", entity != null, entity);
} else {
log.debug("Search result: [{}]", entity != null);
}
return entity;
}
@Override
public List<EventEntity> findEvents(UUID tenantId, EntityId entityId, TimePageLink pageLink) {
log.trace("Try to find events by tenant [{}], entity [{}]and pageLink [{}]", tenantId, entityId, pageLink);
List<EventEntity> entities = findPageWithTimeSearch(EVENT_BY_ID_VIEW_NAME,
Arrays.asList(eq(ModelConstants.EVENT_TENANT_ID_PROPERTY, tenantId),
eq(ModelConstants.EVENT_ENTITY_TYPE_PROPERTY, entityId.getEntityType()),
eq(ModelConstants.EVENT_ENTITY_ID_PROPERTY, entityId.getId())),
pageLink);
log.trace("Found events by tenant [{}], entity [{}] and pageLink [{}]", tenantId, entityId, pageLink);
return entities;
}
@Override
public List<EventEntity> findEvents(UUID tenantId, EntityId entityId, String eventType, TimePageLink pageLink) {
log.trace("Try to find events by tenant [{}], entity [{}], type [{}] and pageLink [{}]", tenantId, entityId, eventType, pageLink);
List<EventEntity> entities = findPageWithTimeSearch(EVENT_BY_TYPE_AND_ID_VIEW_NAME,
Arrays.asList(eq(ModelConstants.EVENT_TENANT_ID_PROPERTY, tenantId),
eq(ModelConstants.EVENT_ENTITY_TYPE_PROPERTY, entityId.getEntityType()),
eq(ModelConstants.EVENT_ENTITY_ID_PROPERTY, entityId.getId()),
eq(ModelConstants.EVENT_TYPE_PROPERTY, eventType)),
pageLink.isAscOrder() ? QueryBuilder.asc(ModelConstants.EVENT_TYPE_PROPERTY) :
QueryBuilder.desc(ModelConstants.EVENT_TYPE_PROPERTY),
pageLink);
log.trace("Found events by tenant [{}], entity [{}], type [{}] and pageLink [{}]", tenantId, entityId, eventType, pageLink);
return entities;
}
private Optional<EventEntity> save(EventEntity entity, boolean ifNotExists) {
if (entity.getId() == null) {
entity.setId(UUIDs.timeBased());
}
Insert insert = QueryBuilder.insertInto(getColumnFamilyName())
.value(ModelConstants.ID_PROPERTY, entity.getId())
.value(ModelConstants.EVENT_TENANT_ID_PROPERTY, entity.getTenantId())
.value(ModelConstants.EVENT_ENTITY_TYPE_PROPERTY, entity.getEntityType())
.value(ModelConstants.EVENT_ENTITY_ID_PROPERTY, entity.getEntityId())
.value(ModelConstants.EVENT_TYPE_PROPERTY, entity.getEventType())
.value(ModelConstants.EVENT_UID_PROPERTY, entity.getEventUId())
.value(ModelConstants.EVENT_BODY_PROPERTY, entity.getBody());
if (ifNotExists) {
insert = insert.ifNotExists();
}
ResultSet rs = executeWrite(insert);
if (rs.wasApplied()) {
return Optional.of(entity);
} else {
return Optional.empty();
}
}
}

130
dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java

@ -0,0 +1,130 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.event;
import com.datastax.driver.core.utils.UUIDs;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.Event;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EventId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TimePageData;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.model.EventEntity;
import org.thingsboard.server.dao.service.DataValidator;
import java.util.List;
import java.util.Optional;
import static org.thingsboard.server.dao.DaoUtil.convertDataList;
import static org.thingsboard.server.dao.DaoUtil.getData;
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
@Service
@Slf4j
public class BaseEventService implements EventService {
private final TenantId systemTenantId = new TenantId(NULL_UUID);
@Autowired
public EventDao eventDao;
@Override
public Event save(Event event) {
eventValidator.validate(event);
if (event.getTenantId() == null) {
log.trace("Save system event with predefined id {}", systemTenantId);
event.setTenantId(systemTenantId);
}
if (event.getId() == null) {
event.setId(new EventId(UUIDs.timeBased()));
}
if (StringUtils.isEmpty(event.getUid())) {
event.setUid(event.getId().toString());
}
return getData(eventDao.save(event));
}
@Override
public Optional<Event> saveIfNotExists(Event event) {
eventValidator.validate(event);
if (StringUtils.isEmpty(event.getUid())) {
throw new DataValidationException("Event uid should be specified!.");
}
if (event.getTenantId() == null) {
log.trace("Save system event with predefined id {}", systemTenantId);
event.setTenantId(systemTenantId);
}
if (event.getId() == null) {
event.setId(new EventId(UUIDs.timeBased()));
}
Optional<EventEntity> result = eventDao.saveIfNotExists(event);
return result.isPresent() ? Optional.of(getData(result.get())) : Optional.empty();
}
@Override
public Optional<Event> findEvent(TenantId tenantId, EntityId entityId, String eventType, String eventUid) {
if (tenantId == null) {
throw new DataValidationException("Tenant id should be specified!.");
}
if (entityId == null) {
throw new DataValidationException("Entity id should be specified!.");
}
if (StringUtils.isEmpty(eventType)) {
throw new DataValidationException("Event type should be specified!.");
}
if (StringUtils.isEmpty(eventUid)) {
throw new DataValidationException("Event uid should be specified!.");
}
EventEntity entity = eventDao.findEvent(tenantId.getId(), entityId, eventType, eventUid);
return entity != null ? Optional.of(getData(entity)) : Optional.empty();
}
@Override
public TimePageData<Event> findEvents(TenantId tenantId, EntityId entityId, TimePageLink pageLink) {
List<EventEntity> entities = eventDao.findEvents(tenantId.getId(), entityId, pageLink);
List<Event> events = convertDataList(entities);
return new TimePageData<Event>(events, pageLink);
}
@Override
public TimePageData<Event> findEvents(TenantId tenantId, EntityId entityId, String eventType, TimePageLink pageLink) {
List<EventEntity> entities = eventDao.findEvents(tenantId.getId(), entityId, eventType, pageLink);
List<Event> events = convertDataList(entities);
return new TimePageData<Event>(events, pageLink);
}
private DataValidator<Event> eventValidator =
new DataValidator<Event>() {
@Override
protected void validateDataImpl(Event event) {
if (event.getEntityId() == null) {
throw new DataValidationException("Entity id should be specified!.");
}
if (StringUtils.isEmpty(event.getType())) {
throw new DataValidationException("Event type should be specified!.");
}
if (event.getBody() == null) {
throw new DataValidationException("Event body should be specified!.");
}
}
};
}

82
dao/src/main/java/org/thingsboard/server/dao/event/EventDao.java

@ -0,0 +1,82 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.event;
import org.thingsboard.server.common.data.Event;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.dao.Dao;
import org.thingsboard.server.dao.model.EventEntity;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
/**
* The Interface DeviceDao.
*
* @param <T> the generic type
*/
public interface EventDao extends Dao<EventEntity> {
/**
* Save or update event object
*
* @param event the event object
* @return saved event object
*/
EventEntity save(Event event);
/**
* Save event object if it is not yet saved
*
* @param event the event object
* @return saved event object
*/
Optional<EventEntity> saveIfNotExists(Event event);
/**
* Find event by tenantId, entityId and eventUid.
*
* @param tenantId the tenantId
* @param entityId the entityId
* @param eventType the eventType
* @param eventUid the eventUid
* @return the event
*/
EventEntity findEvent(UUID tenantId, EntityId entityId, String eventType, String eventUid);
/**
* Find events by tenantId, entityId and pageLink.
*
* @param tenantId the tenantId
* @param entityId the entityId
* @param pageLink the pageLink
* @return the event list
*/
List<EventEntity> findEvents(UUID tenantId, EntityId entityId, TimePageLink pageLink);
/**
* Find events by tenantId, entityId, eventType and pageLink.
*
* @param tenantId the tenantId
* @param entityId the entityId
* @param eventType the eventType
* @param pageLink the pageLink
* @return the event list
*/
List<EventEntity> findEvents(UUID tenantId, EntityId entityId, String eventType, TimePageLink pageLink);
}

38
dao/src/main/java/org/thingsboard/server/dao/event/EventService.java

@ -0,0 +1,38 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.event;
import org.thingsboard.server.common.data.Event;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TimePageData;
import org.thingsboard.server.common.data.page.TimePageLink;
import java.util.List;
import java.util.Optional;
public interface EventService {
Event save(Event event);
Optional<Event> saveIfNotExists(Event event);
Optional<Event> findEvent(TenantId tenantId, EntityId entityId, String eventType, String eventUid);
TimePageData<Event> findEvents(TenantId tenantId, EntityId entityId, TimePageLink pageLink);
TimePageData<Event> findEvents(TenantId tenantId, EntityId entityId, String eventType, TimePageLink pageLink);
}

29
dao/src/main/java/org/thingsboard/server/dao/exception/DataValidationException.java

@ -0,0 +1,29 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.exception;
public class DataValidationException extends RuntimeException {
private static final long serialVersionUID = 7659985660312721830L;
public DataValidationException(String message) {
super(message);
}
public DataValidationException(String message, Throwable cause) {
super(message, cause);
}
}

38
dao/src/main/java/org/thingsboard/server/dao/exception/DatabaseException.java

@ -0,0 +1,38 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.exception;
public class DatabaseException extends RuntimeException {
private static final long serialVersionUID = 3463762014441887103L;
public DatabaseException() {
super();
}
public DatabaseException(String message, Throwable cause) {
super(message, cause);
}
public DatabaseException(String message) {
super(message);
}
public DatabaseException(Throwable cause) {
super(cause);
}
}

30
dao/src/main/java/org/thingsboard/server/dao/exception/IncorrectParameterException.java

@ -0,0 +1,30 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.exception;
public class IncorrectParameterException extends RuntimeException {
private static final long serialVersionUID = 601995650578985289L;
public IncorrectParameterException(String message) {
super(message);
}
public IncorrectParameterException(String message, Throwable cause) {
super(message, cause);
}
}

147
dao/src/main/java/org/thingsboard/server/dao/model/AdminSettingsEntity.java

@ -0,0 +1,147 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.model;
import static org.thingsboard.server.dao.model.ModelConstants.ADMIN_SETTINGS_COLUMN_FAMILY_NAME;
import static org.thingsboard.server.dao.model.ModelConstants.ADMIN_SETTINGS_JSON_VALUE_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ADMIN_SETTINGS_KEY_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY;
import java.util.UUID;
import org.thingsboard.server.common.data.AdminSettings;
import org.thingsboard.server.common.data.id.AdminSettingsId;
import org.thingsboard.server.dao.model.type.JsonCodec;
import com.datastax.driver.core.utils.UUIDs;
import com.datastax.driver.mapping.annotations.Column;
import com.datastax.driver.mapping.annotations.PartitionKey;
import com.datastax.driver.mapping.annotations.Table;
import com.datastax.driver.mapping.annotations.Transient;
import com.fasterxml.jackson.databind.JsonNode;
@Table(name = ADMIN_SETTINGS_COLUMN_FAMILY_NAME)
public final class AdminSettingsEntity implements BaseEntity<AdminSettings> {
@Transient
private static final long serialVersionUID = 899117723388310403L;
@PartitionKey(value = 0)
@Column(name = ID_PROPERTY)
private UUID id;
@Column(name = ADMIN_SETTINGS_KEY_PROPERTY)
private String key;
@Column(name = ADMIN_SETTINGS_JSON_VALUE_PROPERTY, codec = JsonCodec.class)
private JsonNode jsonValue;
public AdminSettingsEntity() {
super();
}
public AdminSettingsEntity(AdminSettings adminSettings) {
if (adminSettings.getId() != null) {
this.id = adminSettings.getId().getId();
}
this.key = adminSettings.getKey();
this.jsonValue = adminSettings.getJsonValue();
}
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public JsonNode getJsonValue() {
return jsonValue;
}
public void setJsonValue(JsonNode jsonValue) {
this.jsonValue = jsonValue;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + ((jsonValue == null) ? 0 : jsonValue.hashCode());
result = prime * result + ((key == null) ? 0 : key.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
AdminSettingsEntity other = (AdminSettingsEntity) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
if (jsonValue == null) {
if (other.jsonValue != null)
return false;
} else if (!jsonValue.equals(other.jsonValue))
return false;
if (key == null) {
if (other.key != null)
return false;
} else if (!key.equals(other.key))
return false;
return true;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("AdminSettingsEntity [id=");
builder.append(id);
builder.append(", key=");
builder.append(key);
builder.append(", jsonValue=");
builder.append(jsonValue);
builder.append("]");
return builder.toString();
}
@Override
public AdminSettings toData() {
AdminSettings adminSettings = new AdminSettings(new AdminSettingsId(id));
adminSettings.setCreatedTime(UUIDs.unixTimestamp(id));
adminSettings.setKey(key);
adminSettings.setJsonValue(jsonValue);
return adminSettings;
}
}

27
dao/src/main/java/org/thingsboard/server/dao/model/BaseEntity.java

@ -0,0 +1,27 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.model;
import java.io.Serializable;
import java.util.UUID;
public interface BaseEntity<D> extends ToData<D>, Serializable {
UUID getId();
void setId(UUID id);
}

162
dao/src/main/java/org/thingsboard/server/dao/model/ComponentDescriptorEntity.java

@ -0,0 +1,162 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.model;
import com.datastax.driver.mapping.annotations.Column;
import com.datastax.driver.mapping.annotations.PartitionKey;
import com.datastax.driver.mapping.annotations.Table;
import com.fasterxml.jackson.databind.JsonNode;
import org.thingsboard.server.common.data.id.ComponentDescriptorId;
import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
import org.thingsboard.server.common.data.plugin.ComponentScope;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.dao.model.type.JsonCodec;
import java.util.UUID;
/**
* @author Andrew Shvayka
*/
@Table(name = ModelConstants.COMPONENT_DESCRIPTOR_COLUMN_FAMILY_NAME)
public class ComponentDescriptorEntity implements SearchTextEntity<ComponentDescriptor> {
private static final long serialVersionUID = 1L;
@PartitionKey
@Column(name = ModelConstants.ID_PROPERTY)
private UUID id;
@Column(name = ModelConstants.COMPONENT_DESCRIPTOR_TYPE_PROPERTY)
private ComponentType type;
@Column(name = ModelConstants.COMPONENT_DESCRIPTOR_SCOPE_PROPERTY)
private ComponentScope scope;
@Column(name = ModelConstants.COMPONENT_DESCRIPTOR_NAME_PROPERTY)
private String name;
@Column(name = ModelConstants.COMPONENT_DESCRIPTOR_CLASS_PROPERTY)
private String clazz;
@Column(name = ModelConstants.COMPONENT_DESCRIPTOR_CONFIGURATION_DESCRIPTOR_PROPERTY, codec = JsonCodec.class)
private JsonNode configurationDescriptor;
@Column(name = ModelConstants.COMPONENT_DESCRIPTOR_ACTIONS_PROPERTY)
private String actions;
@Column(name = ModelConstants.SEARCH_TEXT_PROPERTY)
private String searchText;
public ComponentDescriptorEntity() {
}
public ComponentDescriptorEntity(ComponentDescriptor component) {
if (component.getId() != null) {
this.id = component.getId().getId();
}
this.actions = component.getActions();
this.type = component.getType();
this.scope = component.getScope();
this.name = component.getName();
this.clazz = component.getClazz();
this.configurationDescriptor = component.getConfigurationDescriptor();
this.searchText = component.getName();
}
@Override
public ComponentDescriptor toData() {
ComponentDescriptor data = new ComponentDescriptor(new ComponentDescriptorId(id));
data.setType(type);
data.setScope(scope);
data.setName(this.getName());
data.setClazz(this.getClazz());
data.setActions(this.getActions());
data.setConfigurationDescriptor(this.getConfigurationDescriptor());
return data;
}
@Override
public UUID getId() {
return id;
}
@Override
public void setId(UUID id) {
this.id = id;
}
public String getActions() {
return actions;
}
public void setActions(String actions) {
this.actions = actions;
}
public ComponentType getType() {
return type;
}
public void setType(ComponentType type) {
this.type = type;
}
public ComponentScope getScope() {
return scope;
}
public void setScope(ComponentScope scope) {
this.scope = scope;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getClazz() {
return clazz;
}
public void setClazz(String clazz) {
this.clazz = clazz;
}
public JsonNode getConfigurationDescriptor() {
return configurationDescriptor;
}
public void setConfigurationDescriptor(JsonNode configurationDescriptor) {
this.configurationDescriptor = configurationDescriptor;
}
public String getSearchText() {
return searchText;
}
@Override
public void setSearchText(String searchText) {
this.searchText = searchText;
}
@Override
public String getSearchTextSource() {
return searchText;
}
}

365
dao/src/main/java/org/thingsboard/server/dao/model/CustomerEntity.java

@ -0,0 +1,365 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.model;
import static org.thingsboard.server.dao.model.ModelConstants.ADDRESS2_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ADDRESS_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.CITY_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.COUNTRY_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.CUSTOMER_ADDITIONAL_INFO_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.CUSTOMER_COLUMN_FAMILY_NAME;
import static org.thingsboard.server.dao.model.ModelConstants.CUSTOMER_TENANT_ID_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.CUSTOMER_TITLE_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.EMAIL_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.PHONE_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.STATE_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ZIP_PROPERTY;
import java.util.UUID;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.model.type.JsonCodec;
import com.datastax.driver.core.utils.UUIDs;
import com.datastax.driver.mapping.annotations.Column;
import com.datastax.driver.mapping.annotations.PartitionKey;
import com.datastax.driver.mapping.annotations.Table;
import com.datastax.driver.mapping.annotations.Transient;
import com.fasterxml.jackson.databind.JsonNode;
@Table(name = CUSTOMER_COLUMN_FAMILY_NAME)
public final class CustomerEntity implements SearchTextEntity<Customer> {
@Transient
private static final long serialVersionUID = -7732527103760948490L;
@PartitionKey(value = 0)
@Column(name = ID_PROPERTY)
private UUID id;
@PartitionKey(value = 1)
@Column(name = CUSTOMER_TENANT_ID_PROPERTY)
private UUID tenantId;
@Column(name = CUSTOMER_TITLE_PROPERTY)
private String title;
@Column(name = SEARCH_TEXT_PROPERTY)
private String searchText;
@Column(name = COUNTRY_PROPERTY)
private String country;
@Column(name = STATE_PROPERTY)
private String state;
@Column(name = CITY_PROPERTY)
private String city;
@Column(name = ADDRESS_PROPERTY)
private String address;
@Column(name = ADDRESS2_PROPERTY)
private String address2;
@Column(name = ZIP_PROPERTY)
private String zip;
@Column(name = PHONE_PROPERTY)
private String phone;
@Column(name = EMAIL_PROPERTY)
private String email;
@Column(name = CUSTOMER_ADDITIONAL_INFO_PROPERTY, codec = JsonCodec.class)
private JsonNode additionalInfo;
public CustomerEntity() {
super();
}
public CustomerEntity(Customer customer) {
if (customer.getId() != null) {
this.id = customer.getId().getId();
}
this.tenantId = customer.getTenantId().getId();
this.title = customer.getTitle();
this.country = customer.getCountry();
this.state = customer.getState();
this.city = customer.getCity();
this.address = customer.getAddress();
this.address2 = customer.getAddress2();
this.zip = customer.getZip();
this.phone = customer.getPhone();
this.email = customer.getEmail();
this.additionalInfo = customer.getAdditionalInfo();
}
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
public UUID getTenantId() {
return tenantId;
}
public void setTenantId(UUID tenantId) {
this.tenantId = tenantId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getAddress2() {
return address2;
}
public void setAddress2(String address2) {
this.address2 = address2;
}
public String getZip() {
return zip;
}
public void setZip(String zip) {
this.zip = zip;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public JsonNode getAdditionalInfo() {
return additionalInfo;
}
public void setAdditionalInfo(JsonNode additionalInfo) {
this.additionalInfo = additionalInfo;
}
@Override
public String getSearchTextSource() {
return title;
}
@Override
public void setSearchText(String searchText) {
this.searchText = searchText;
}
public String getSearchText() {
return searchText;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((additionalInfo == null) ? 0 : additionalInfo.hashCode());
result = prime * result + ((address == null) ? 0 : address.hashCode());
result = prime * result + ((address2 == null) ? 0 : address2.hashCode());
result = prime * result + ((city == null) ? 0 : city.hashCode());
result = prime * result + ((country == null) ? 0 : country.hashCode());
result = prime * result + ((email == null) ? 0 : email.hashCode());
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + ((phone == null) ? 0 : phone.hashCode());
result = prime * result + ((state == null) ? 0 : state.hashCode());
result = prime * result + ((tenantId == null) ? 0 : tenantId.hashCode());
result = prime * result + ((title == null) ? 0 : title.hashCode());
result = prime * result + ((zip == null) ? 0 : zip.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
CustomerEntity other = (CustomerEntity) obj;
if (additionalInfo == null) {
if (other.additionalInfo != null)
return false;
} else if (!additionalInfo.equals(other.additionalInfo))
return false;
if (address == null) {
if (other.address != null)
return false;
} else if (!address.equals(other.address))
return false;
if (address2 == null) {
if (other.address2 != null)
return false;
} else if (!address2.equals(other.address2))
return false;
if (city == null) {
if (other.city != null)
return false;
} else if (!city.equals(other.city))
return false;
if (country == null) {
if (other.country != null)
return false;
} else if (!country.equals(other.country))
return false;
if (email == null) {
if (other.email != null)
return false;
} else if (!email.equals(other.email))
return false;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
if (phone == null) {
if (other.phone != null)
return false;
} else if (!phone.equals(other.phone))
return false;
if (state == null) {
if (other.state != null)
return false;
} else if (!state.equals(other.state))
return false;
if (tenantId == null) {
if (other.tenantId != null)
return false;
} else if (!tenantId.equals(other.tenantId))
return false;
if (title == null) {
if (other.title != null)
return false;
} else if (!title.equals(other.title))
return false;
if (zip == null) {
if (other.zip != null)
return false;
} else if (!zip.equals(other.zip))
return false;
return true;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("CustomerEntity [id=");
builder.append(id);
builder.append(", tenantId=");
builder.append(tenantId);
builder.append(", title=");
builder.append(title);
builder.append(", country=");
builder.append(country);
builder.append(", state=");
builder.append(state);
builder.append(", city=");
builder.append(city);
builder.append(", address=");
builder.append(address);
builder.append(", address2=");
builder.append(address2);
builder.append(", zip=");
builder.append(zip);
builder.append(", phone=");
builder.append(phone);
builder.append(", email=");
builder.append(email);
builder.append(", additionalInfo=");
builder.append(additionalInfo);
builder.append("]");
return builder.toString();
}
@Override
public Customer toData() {
Customer customer = new Customer(new CustomerId(id));
customer.setCreatedTime(UUIDs.unixTimestamp(id));
customer.setTenantId(new TenantId(tenantId));
customer.setTitle(title);
customer.setCountry(country);
customer.setState(state);
customer.setCity(city);
customer.setAddress(address);
customer.setAddress2(address2);
customer.setZip(zip);
customer.setPhone(phone);
customer.setEmail(email);
customer.setAdditionalInfo(additionalInfo);
return customer;
}
}

221
dao/src/main/java/org/thingsboard/server/dao/model/DashboardEntity.java

@ -0,0 +1,221 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.model;
import java.util.UUID;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.model.type.JsonCodec;
import com.datastax.driver.core.utils.UUIDs;
import com.datastax.driver.mapping.annotations.Column;
import com.datastax.driver.mapping.annotations.PartitionKey;
import com.datastax.driver.mapping.annotations.Table;
import com.datastax.driver.mapping.annotations.Transient;
import com.fasterxml.jackson.databind.JsonNode;
@Table(name = ModelConstants.DASHBOARD_COLUMN_FAMILY_NAME)
public final class DashboardEntity implements SearchTextEntity<Dashboard> {
@Transient
private static final long serialVersionUID = 2998395951247446191L;
@PartitionKey(value = 0)
@Column(name = ModelConstants.ID_PROPERTY)
private UUID id;
@PartitionKey(value = 1)
@Column(name = ModelConstants.DASHBOARD_TENANT_ID_PROPERTY)
private UUID tenantId;
@PartitionKey(value = 2)
@Column(name = ModelConstants.DASHBOARD_CUSTOMER_ID_PROPERTY)
private UUID customerId;
@Column(name = ModelConstants.DASHBOARD_TITLE_PROPERTY)
private String title;
@Column(name = ModelConstants.SEARCH_TEXT_PROPERTY)
private String searchText;
@Column(name = ModelConstants.DASHBOARD_CONFIGURATION_PROPERTY, codec = JsonCodec.class)
private JsonNode configuration;
public DashboardEntity() {
super();
}
public DashboardEntity(Dashboard dashboard) {
if (dashboard.getId() != null) {
this.id = dashboard.getId().getId();
}
if (dashboard.getTenantId() != null) {
this.tenantId = dashboard.getTenantId().getId();
}
if (dashboard.getCustomerId() != null) {
this.customerId = dashboard.getCustomerId().getId();
}
this.title = dashboard.getTitle();
this.configuration = dashboard.getConfiguration();
}
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
public UUID getTenantId() {
return tenantId;
}
public void setTenantId(UUID tenantId) {
this.tenantId = tenantId;
}
public UUID getCustomerId() {
return customerId;
}
public void setCustomerId(UUID customerId) {
this.customerId = customerId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public JsonNode getConfiguration() {
return configuration;
}
public void setConfiguration(JsonNode configuration) {
this.configuration = configuration;
}
@Override
public String getSearchTextSource() {
return title;
}
@Override
public void setSearchText(String searchText) {
this.searchText = searchText;
}
public String getSearchText() {
return searchText;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((configuration == null) ? 0 : configuration.hashCode());
result = prime * result + ((customerId == null) ? 0 : customerId.hashCode());
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + ((searchText == null) ? 0 : searchText.hashCode());
result = prime * result + ((tenantId == null) ? 0 : tenantId.hashCode());
result = prime * result + ((title == null) ? 0 : title.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
DashboardEntity other = (DashboardEntity) obj;
if (configuration == null) {
if (other.configuration != null)
return false;
} else if (!configuration.equals(other.configuration))
return false;
if (customerId == null) {
if (other.customerId != null)
return false;
} else if (!customerId.equals(other.customerId))
return false;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
if (searchText == null) {
if (other.searchText != null)
return false;
} else if (!searchText.equals(other.searchText))
return false;
if (tenantId == null) {
if (other.tenantId != null)
return false;
} else if (!tenantId.equals(other.tenantId))
return false;
if (title == null) {
if (other.title != null)
return false;
} else if (!title.equals(other.title))
return false;
return true;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("DashboardEntity [id=");
builder.append(id);
builder.append(", tenantId=");
builder.append(tenantId);
builder.append(", customerId=");
builder.append(customerId);
builder.append(", title=");
builder.append(title);
builder.append(", searchText=");
builder.append(searchText);
builder.append(", configuration=");
builder.append(configuration);
builder.append("]");
return builder.toString();
}
@Override
public Dashboard toData() {
Dashboard dashboard = new Dashboard(new DashboardId(id));
dashboard.setCreatedTime(UUIDs.unixTimestamp(id));
if (tenantId != null) {
dashboard.setTenantId(new TenantId(tenantId));
}
if (customerId != null) {
dashboard.setCustomerId(new CustomerId(customerId));
}
dashboard.setTitle(title);
dashboard.setConfiguration(configuration);
return dashboard;
}
}

186
dao/src/main/java/org/thingsboard/server/dao/model/DeviceCredentialsEntity.java

@ -0,0 +1,186 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.model;
import java.util.UUID;
import org.thingsboard.server.common.data.id.DeviceCredentialsId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
import org.thingsboard.server.dao.model.type.DeviceCredentialsTypeCodec;
import com.datastax.driver.core.utils.UUIDs;
import com.datastax.driver.mapping.annotations.Column;
import com.datastax.driver.mapping.annotations.PartitionKey;
import com.datastax.driver.mapping.annotations.Table;
import com.datastax.driver.mapping.annotations.Transient;
@Table(name = ModelConstants.DEVICE_CREDENTIALS_COLUMN_FAMILY_NAME)
public final class DeviceCredentialsEntity implements BaseEntity<DeviceCredentials> {
@Transient
private static final long serialVersionUID = -2667310560260623272L;
@PartitionKey(value = 0)
@Column(name = ModelConstants.ID_PROPERTY)
private UUID id;
@Column(name = ModelConstants.DEVICE_CREDENTIALS_DEVICE_ID_PROPERTY)
private UUID deviceId;
@Column(name = ModelConstants.DEVICE_CREDENTIALS_CREDENTIALS_TYPE_PROPERTY, codec = DeviceCredentialsTypeCodec.class)
private DeviceCredentialsType credentialsType;
@Column(name = ModelConstants.DEVICE_CREDENTIALS_CREDENTIALS_ID_PROPERTY)
private String credentialsId;
@Column(name = ModelConstants.DEVICE_CREDENTIALS_CREDENTIALS_VALUE_PROPERTY)
private String credentialsValue;
public DeviceCredentialsEntity() {
super();
}
public DeviceCredentialsEntity(DeviceCredentials deviceCredentials) {
if (deviceCredentials.getId() != null) {
this.id = deviceCredentials.getId().getId();
}
if (deviceCredentials.getDeviceId() != null) {
this.deviceId = deviceCredentials.getDeviceId().getId();
}
this.credentialsType = deviceCredentials.getCredentialsType();
this.credentialsId = deviceCredentials.getCredentialsId();
this.credentialsValue = deviceCredentials.getCredentialsValue();
}
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
public UUID getDeviceId() {
return deviceId;
}
public void setDeviceId(UUID deviceId) {
this.deviceId = deviceId;
}
public DeviceCredentialsType getCredentialsType() {
return credentialsType;
}
public void setCredentialsType(DeviceCredentialsType credentialsType) {
this.credentialsType = credentialsType;
}
public String getCredentialsId() {
return credentialsId;
}
public void setCredentialsId(String credentialsId) {
this.credentialsId = credentialsId;
}
public String getCredentialsValue() {
return credentialsValue;
}
public void setCredentialsValue(String credentialsValue) {
this.credentialsValue = credentialsValue;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((credentialsId == null) ? 0 : credentialsId.hashCode());
result = prime * result + ((credentialsType == null) ? 0 : credentialsType.hashCode());
result = prime * result + ((credentialsValue == null) ? 0 : credentialsValue.hashCode());
result = prime * result + ((deviceId == null) ? 0 : deviceId.hashCode());
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
DeviceCredentialsEntity other = (DeviceCredentialsEntity) obj;
if (credentialsId == null) {
if (other.credentialsId != null)
return false;
} else if (!credentialsId.equals(other.credentialsId))
return false;
if (credentialsType != other.credentialsType)
return false;
if (credentialsValue == null) {
if (other.credentialsValue != null)
return false;
} else if (!credentialsValue.equals(other.credentialsValue))
return false;
if (deviceId == null) {
if (other.deviceId != null)
return false;
} else if (!deviceId.equals(other.deviceId))
return false;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("DeviceCredentialsEntity [id=");
builder.append(id);
builder.append(", deviceId=");
builder.append(deviceId);
builder.append(", credentialsType=");
builder.append(credentialsType);
builder.append(", credentialsId=");
builder.append(credentialsId);
builder.append(", credentialsValue=");
builder.append(credentialsValue);
builder.append("]");
return builder.toString();
}
@Override
public DeviceCredentials toData() {
DeviceCredentials deviceCredentials = new DeviceCredentials(new DeviceCredentialsId(id));
deviceCredentials.setCreatedTime(UUIDs.unixTimestamp(id));
if (deviceId != null) {
deviceCredentials.setDeviceId(new DeviceId(deviceId));
}
deviceCredentials.setCredentialsType(credentialsType);
deviceCredentials.setCredentialsId(credentialsId);
deviceCredentials.setCredentialsValue(credentialsValue);
return deviceCredentials;
}
}

214
dao/src/main/java/org/thingsboard/server/dao/model/DeviceEntity.java

@ -0,0 +1,214 @@
/**
* Copyright © 2016 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.model;
import com.datastax.driver.core.utils.UUIDs;
import com.datastax.driver.mapping.annotations.Column;
import com.datastax.driver.mapping.annotations.PartitionKey;
import com.datastax.driver.mapping.annotations.Table;
import com.datastax.driver.mapping.annotations.Transient;
import com.fasterxml.jackson.databind.JsonNode;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.model.type.JsonCodec;
import java.util.UUID;
import static org.thingsboard.server.dao.model.ModelConstants.*;
@Table(name = DEVICE_COLUMN_FAMILY_NAME)
public final class DeviceEntity implements SearchTextEntity<Device> {
@Transient
private static final long serialVersionUID = -1265181166886910152L;
@PartitionKey(value = 0)
@Column(name = ID_PROPERTY)
private UUID id;
@PartitionKey(value = 1)
@Column(name = DEVICE_TENANT_ID_PROPERTY)
private UUID tenantId;
@PartitionKey(value = 2)
@Column(name = DEVICE_CUSTOMER_ID_PROPERTY)
private UUID customerId;
@Column(name = DEVICE_NAME_PROPERTY)
private String name;
@Column(name = SEARCH_TEXT_PROPERTY)
private String searchText;
@Column(name = DEVICE_ADDITIONAL_INFO_PROPERTY, codec = JsonCodec.class)
private JsonNode additionalInfo;
public DeviceEntity() {
super();
}
public DeviceEntity(Device device) {
if (device.getId() != null) {
this.id = device.getId().getId();
}
if (device.getTenantId() != null) {
this.tenantId = device.getTenantId().getId();
}
if (device.getCustomerId() != null) {
this.customerId = device.getCustomerId().getId();
}
this.name = device.getName();
this.additionalInfo = device.getAdditionalInfo();
}
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
public UUID getTenantId() {
return tenantId;
}
public void setTenantId(UUID tenantId) {
this.tenantId = tenantId;
}
public UUID getCustomerId() {
return customerId;
}
public void setCustomerId(UUID customerId) {
this.customerId = customerId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public JsonNode getAdditionalInfo() {
return additionalInfo;
}
public void setAdditionalInfo(JsonNode additionalInfo) {
this.additionalInfo = additionalInfo;
}
@Override
public String getSearchTextSource() {
return name;
}
@Override
public void setSearchText(String searchText) {
this.searchText = searchText;
}
public String getSearchText() {
return searchText;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((additionalInfo == null) ? 0 : additionalInfo.hashCode());
result = prime * result + ((customerId == null) ? 0 : customerId.hashCode());
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((tenantId == null) ? 0 : tenantId.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
DeviceEntity other = (DeviceEntity) obj;
if (additionalInfo == null) {
if (other.additionalInfo != null)
return false;
} else if (!additionalInfo.equals(other.additionalInfo))
return false;
if (customerId == null) {
if (other.customerId != null)
return false;
} else if (!customerId.equals(other.customerId))
return false;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (tenantId == null) {
if (other.tenantId != null)
return false;
} else if (!tenantId.equals(other.tenantId))
return false;
return true;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("DeviceEntity [id=");
builder.append(id);
builder.append(", tenantId=");
builder.append(tenantId);
builder.append(", customerId=");
builder.append(customerId);
builder.append(", name=");
builder.append(name);
builder.append(", additionalInfo=");
builder.append(additionalInfo);
builder.append("]");
return builder.toString();
}
@Override
public Device toData() {
Device device = new Device(new DeviceId(id));
device.setCreatedTime(UUIDs.unixTimestamp(id));
if (tenantId != null) {
device.setTenantId(new TenantId(tenantId));
}
if (customerId != null) {
device.setCustomerId(new CustomerId(customerId));
}
device.setName(name);
device.setAdditionalInfo(additionalInfo);
return device;
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save