Commit 5fdbb51cd7acfff633b62446c85be4ae6d36d771
1 parent
c22bf33d
DAO layer, services, configuration and UI
Showing
54 changed files
with
4754 additions
and
0 deletions
Too many changes to show.
To preserve performance only 54 of 495 files are displayed.
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.config; | ||
17 | + | ||
18 | +import org.springframework.boot.context.properties.ConfigurationProperties; | ||
19 | +import org.springframework.context.annotation.Configuration; | ||
20 | +import org.thingsboard.server.service.security.model.token.JwtToken; | ||
21 | + | ||
22 | +@Configuration | ||
23 | +@ConfigurationProperties(prefix = "security.jwt") | ||
24 | +public class JwtSettings { | ||
25 | + /** | ||
26 | + * {@link JwtToken} will expire after this time. | ||
27 | + */ | ||
28 | + private Integer tokenExpirationTime; | ||
29 | + | ||
30 | + /** | ||
31 | + * Token issuer. | ||
32 | + */ | ||
33 | + private String tokenIssuer; | ||
34 | + | ||
35 | + /** | ||
36 | + * Key is used to sign {@link JwtToken}. | ||
37 | + */ | ||
38 | + private String tokenSigningKey; | ||
39 | + | ||
40 | + /** | ||
41 | + * {@link JwtToken} can be refreshed during this timeframe. | ||
42 | + */ | ||
43 | + private Integer refreshTokenExpTime; | ||
44 | + | ||
45 | + public Integer getRefreshTokenExpTime() { | ||
46 | + return refreshTokenExpTime; | ||
47 | + } | ||
48 | + | ||
49 | + public void setRefreshTokenExpTime(Integer refreshTokenExpTime) { | ||
50 | + this.refreshTokenExpTime = refreshTokenExpTime; | ||
51 | + } | ||
52 | + | ||
53 | + public Integer getTokenExpirationTime() { | ||
54 | + return tokenExpirationTime; | ||
55 | + } | ||
56 | + | ||
57 | + public void setTokenExpirationTime(Integer tokenExpirationTime) { | ||
58 | + this.tokenExpirationTime = tokenExpirationTime; | ||
59 | + } | ||
60 | + | ||
61 | + public String getTokenIssuer() { | ||
62 | + return tokenIssuer; | ||
63 | + } | ||
64 | + public void setTokenIssuer(String tokenIssuer) { | ||
65 | + this.tokenIssuer = tokenIssuer; | ||
66 | + } | ||
67 | + | ||
68 | + public String getTokenSigningKey() { | ||
69 | + return tokenSigningKey; | ||
70 | + } | ||
71 | + | ||
72 | + public void setTokenSigningKey(String tokenSigningKey) { | ||
73 | + this.tokenSigningKey = tokenSigningKey; | ||
74 | + } | ||
75 | +} |
application/src/main/java/org/thingsboard/server/config/ThingsboardMessageConfiguration.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.config; | ||
17 | + | ||
18 | +import org.springframework.context.MessageSource; | ||
19 | +import org.springframework.context.annotation.Bean; | ||
20 | +import org.springframework.context.annotation.Configuration; | ||
21 | +import org.springframework.context.support.ResourceBundleMessageSource; | ||
22 | + | ||
23 | +@Configuration | ||
24 | +public class ThingsboardMessageConfiguration { | ||
25 | + | ||
26 | + @Bean | ||
27 | + public MessageSource messageSource() { | ||
28 | + ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); | ||
29 | + messageSource.setBasename("i18n/messages"); | ||
30 | + messageSource.setDefaultEncoding("UTF-8"); | ||
31 | + return messageSource; | ||
32 | + } | ||
33 | + | ||
34 | +} |
application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.config; | ||
17 | + | ||
18 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
19 | +import org.springframework.beans.factory.annotation.Autowired; | ||
20 | +import org.springframework.beans.factory.annotation.Qualifier; | ||
21 | +import org.springframework.boot.autoconfigure.security.SecurityProperties; | ||
22 | +import org.springframework.context.annotation.Bean; | ||
23 | +import org.springframework.context.annotation.Configuration; | ||
24 | +import org.springframework.core.annotation.Order; | ||
25 | +import org.springframework.security.authentication.AuthenticationManager; | ||
26 | +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; | ||
27 | +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; | ||
28 | +import org.springframework.security.config.annotation.web.builders.HttpSecurity; | ||
29 | +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; | ||
30 | +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; | ||
31 | +import org.springframework.security.config.http.SessionCreationPolicy; | ||
32 | +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; | ||
33 | +import org.springframework.security.web.authentication.AuthenticationFailureHandler; | ||
34 | +import org.springframework.security.web.authentication.AuthenticationSuccessHandler; | ||
35 | +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; | ||
36 | +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; | ||
37 | +import org.thingsboard.server.exception.ThingsboardErrorResponseHandler; | ||
38 | +import org.thingsboard.server.service.security.auth.rest.RestAuthenticationProvider; | ||
39 | +import org.thingsboard.server.service.security.auth.rest.RestLoginProcessingFilter; | ||
40 | +import org.thingsboard.server.service.security.auth.jwt.*; | ||
41 | +import org.thingsboard.server.service.security.auth.jwt.extractor.TokenExtractor; | ||
42 | + | ||
43 | +import java.util.ArrayList; | ||
44 | +import java.util.Arrays; | ||
45 | +import java.util.List; | ||
46 | + | ||
47 | +@Configuration | ||
48 | +@EnableWebSecurity | ||
49 | +@EnableGlobalMethodSecurity(prePostEnabled=true) | ||
50 | +@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER) | ||
51 | +public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapter { | ||
52 | + | ||
53 | + public static final String JWT_TOKEN_HEADER_PARAM = "X-Authorization"; | ||
54 | + public static final String JWT_TOKEN_QUERY_PARAM = "token"; | ||
55 | + | ||
56 | + public static final String DEVICE_API_ENTRY_POINT = "/api/v1/**"; | ||
57 | + public static final String FORM_BASED_LOGIN_ENTRY_POINT = "/api/auth/login"; | ||
58 | + public static final String TOKEN_REFRESH_ENTRY_POINT = "/api/auth/token"; | ||
59 | + public static final String[] NON_TOKEN_BASED_AUTH_ENTRY_POINTS = new String[] {"/index.html", "/static/**", "/api/noauth/**"}; | ||
60 | + public static final String TOKEN_BASED_AUTH_ENTRY_POINT = "/api/**"; | ||
61 | + public static final String WS_TOKEN_BASED_AUTH_ENTRY_POINT = "/api/ws/**"; | ||
62 | + | ||
63 | + @Autowired private ThingsboardErrorResponseHandler restAccessDeniedHandler; | ||
64 | + @Autowired private AuthenticationSuccessHandler successHandler; | ||
65 | + @Autowired private AuthenticationFailureHandler failureHandler; | ||
66 | + @Autowired private RestAuthenticationProvider restAuthenticationProvider; | ||
67 | + @Autowired private JwtAuthenticationProvider jwtAuthenticationProvider; | ||
68 | + @Autowired private RefreshTokenAuthenticationProvider refreshTokenAuthenticationProvider; | ||
69 | + | ||
70 | + @Autowired | ||
71 | + @Qualifier("jwtHeaderTokenExtractor") | ||
72 | + private TokenExtractor jwtHeaderTokenExtractor; | ||
73 | + | ||
74 | + @Autowired | ||
75 | + @Qualifier("jwtQueryTokenExtractor") | ||
76 | + private TokenExtractor jwtQueryTokenExtractor; | ||
77 | + | ||
78 | + @Autowired private AuthenticationManager authenticationManager; | ||
79 | + | ||
80 | + @Autowired private ObjectMapper objectMapper; | ||
81 | + | ||
82 | + @Bean | ||
83 | + protected RestLoginProcessingFilter buildRestLoginProcessingFilter() throws Exception { | ||
84 | + RestLoginProcessingFilter filter = new RestLoginProcessingFilter(FORM_BASED_LOGIN_ENTRY_POINT, successHandler, failureHandler, objectMapper); | ||
85 | + filter.setAuthenticationManager(this.authenticationManager); | ||
86 | + return filter; | ||
87 | + } | ||
88 | + | ||
89 | + @Bean | ||
90 | + protected JwtTokenAuthenticationProcessingFilter buildJwtTokenAuthenticationProcessingFilter() throws Exception { | ||
91 | + List<String> pathsToSkip = new ArrayList(Arrays.asList(NON_TOKEN_BASED_AUTH_ENTRY_POINTS)); | ||
92 | + pathsToSkip.addAll(Arrays.asList(WS_TOKEN_BASED_AUTH_ENTRY_POINT, TOKEN_REFRESH_ENTRY_POINT, FORM_BASED_LOGIN_ENTRY_POINT, DEVICE_API_ENTRY_POINT)); | ||
93 | + SkipPathRequestMatcher matcher = new SkipPathRequestMatcher(pathsToSkip, TOKEN_BASED_AUTH_ENTRY_POINT); | ||
94 | + JwtTokenAuthenticationProcessingFilter filter | ||
95 | + = new JwtTokenAuthenticationProcessingFilter(failureHandler, jwtHeaderTokenExtractor, matcher); | ||
96 | + filter.setAuthenticationManager(this.authenticationManager); | ||
97 | + return filter; | ||
98 | + } | ||
99 | + | ||
100 | + @Bean | ||
101 | + protected RefreshTokenProcessingFilter buildRefreshTokenProcessingFilter() throws Exception { | ||
102 | + RefreshTokenProcessingFilter filter = new RefreshTokenProcessingFilter(TOKEN_REFRESH_ENTRY_POINT, successHandler, failureHandler, objectMapper); | ||
103 | + filter.setAuthenticationManager(this.authenticationManager); | ||
104 | + return filter; | ||
105 | + } | ||
106 | + | ||
107 | + @Bean | ||
108 | + protected JwtTokenAuthenticationProcessingFilter buildWsJwtTokenAuthenticationProcessingFilter() throws Exception { | ||
109 | + AntPathRequestMatcher matcher = new AntPathRequestMatcher(WS_TOKEN_BASED_AUTH_ENTRY_POINT); | ||
110 | + JwtTokenAuthenticationProcessingFilter filter | ||
111 | + = new JwtTokenAuthenticationProcessingFilter(failureHandler, jwtQueryTokenExtractor, matcher); | ||
112 | + filter.setAuthenticationManager(this.authenticationManager); | ||
113 | + return filter; | ||
114 | + } | ||
115 | + | ||
116 | + @Bean | ||
117 | + @Override | ||
118 | + public AuthenticationManager authenticationManagerBean() throws Exception { | ||
119 | + return super.authenticationManagerBean(); | ||
120 | + } | ||
121 | + | ||
122 | + @Override | ||
123 | + protected void configure(AuthenticationManagerBuilder auth) { | ||
124 | + auth.authenticationProvider(restAuthenticationProvider); | ||
125 | + auth.authenticationProvider(jwtAuthenticationProvider); | ||
126 | + auth.authenticationProvider(refreshTokenAuthenticationProvider); | ||
127 | + } | ||
128 | + | ||
129 | + @Bean | ||
130 | + protected BCryptPasswordEncoder passwordEncoder() { | ||
131 | + return new BCryptPasswordEncoder(); | ||
132 | + } | ||
133 | + | ||
134 | + @Override | ||
135 | + protected void configure(HttpSecurity http) throws Exception { | ||
136 | + http.headers().frameOptions().disable() | ||
137 | + .and() | ||
138 | + .csrf().disable() | ||
139 | + .exceptionHandling() | ||
140 | + .and() | ||
141 | + .sessionManagement() | ||
142 | + .sessionCreationPolicy(SessionCreationPolicy.STATELESS) | ||
143 | + .and() | ||
144 | + .authorizeRequests() | ||
145 | + .antMatchers(DEVICE_API_ENTRY_POINT).permitAll() // Device HTTP Transport API | ||
146 | + .antMatchers(FORM_BASED_LOGIN_ENTRY_POINT).permitAll() // Login end-point | ||
147 | + .antMatchers(TOKEN_REFRESH_ENTRY_POINT).permitAll() // Token refresh end-point | ||
148 | + .antMatchers(NON_TOKEN_BASED_AUTH_ENTRY_POINTS).permitAll() // static resources, user activation and password reset end-points | ||
149 | + .and() | ||
150 | + .authorizeRequests() | ||
151 | + .antMatchers(WS_TOKEN_BASED_AUTH_ENTRY_POINT).authenticated() // Protected WebSocket API End-points | ||
152 | + .antMatchers(TOKEN_BASED_AUTH_ENTRY_POINT).authenticated() // Protected API End-points | ||
153 | + .and() | ||
154 | + .exceptionHandling().accessDeniedHandler(restAccessDeniedHandler) | ||
155 | + .and() | ||
156 | + .addFilterBefore(buildRestLoginProcessingFilter(), UsernamePasswordAuthenticationFilter.class) | ||
157 | + .addFilterBefore(buildJwtTokenAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class) | ||
158 | + .addFilterBefore(buildRefreshTokenProcessingFilter(), UsernamePasswordAuthenticationFilter.class) | ||
159 | + .addFilterBefore(buildWsJwtTokenAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class); | ||
160 | + } | ||
161 | +} |
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.config; | ||
17 | + | ||
18 | +import org.springframework.stereotype.Controller; | ||
19 | +import org.springframework.web.bind.annotation.RequestMapping; | ||
20 | + | ||
21 | +@Controller | ||
22 | +public class WebConfig { | ||
23 | + | ||
24 | + @RequestMapping(value = "/{path:^(?!api$)(?!static$)[^\\.]*}/**") | ||
25 | + public String redirect() { | ||
26 | + return "forward:/index.html"; | ||
27 | + } | ||
28 | + | ||
29 | +} |
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.config; | ||
17 | + | ||
18 | +import java.util.Map; | ||
19 | + | ||
20 | +import org.thingsboard.server.exception.ThingsboardErrorCode; | ||
21 | +import org.thingsboard.server.exception.ThingsboardException; | ||
22 | +import org.thingsboard.server.controller.plugin.PluginWebSocketHandler; | ||
23 | +import org.thingsboard.server.service.security.model.SecurityUser; | ||
24 | +import org.springframework.context.annotation.Bean; | ||
25 | +import org.springframework.context.annotation.Configuration; | ||
26 | +import org.springframework.http.HttpStatus; | ||
27 | +import org.springframework.http.server.ServerHttpRequest; | ||
28 | +import org.springframework.http.server.ServerHttpResponse; | ||
29 | +import org.springframework.security.core.Authentication; | ||
30 | +import org.springframework.security.core.context.SecurityContextHolder; | ||
31 | +import org.springframework.web.socket.WebSocketHandler; | ||
32 | +import org.springframework.web.socket.config.annotation.EnableWebSocket; | ||
33 | +import org.springframework.web.socket.config.annotation.WebSocketConfigurer; | ||
34 | +import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; | ||
35 | +import org.springframework.web.socket.server.HandshakeInterceptor; | ||
36 | +import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean; | ||
37 | +import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor; | ||
38 | + | ||
39 | +@Configuration | ||
40 | +@EnableWebSocket | ||
41 | +public class WebSocketConfiguration implements WebSocketConfigurer { | ||
42 | + | ||
43 | + public static final String WS_PLUGIN_PREFIX = "/api/ws/plugins/"; | ||
44 | + public static final String WS_SECURITY_USER_ATTRIBUTE = "SECURITY_USER"; | ||
45 | + private static final String WS_PLUGIN_MAPPING = WS_PLUGIN_PREFIX + "**"; | ||
46 | + | ||
47 | + @Bean | ||
48 | + public ServletServerContainerFactoryBean createWebSocketContainer() { | ||
49 | + ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean(); | ||
50 | + container.setMaxTextMessageBufferSize(8192); | ||
51 | + container.setMaxBinaryMessageBufferSize(8192); | ||
52 | + return container; | ||
53 | + } | ||
54 | + | ||
55 | + @Override | ||
56 | + public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { | ||
57 | + registry.addHandler(pluginWsHandler(), WS_PLUGIN_MAPPING).setAllowedOrigins("*") | ||
58 | + .addInterceptors(new HttpSessionHandshakeInterceptor(), new HandshakeInterceptor() { | ||
59 | + | ||
60 | + @Override | ||
61 | + public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, | ||
62 | + Map<String, Object> attributes) throws Exception { | ||
63 | + SecurityUser user = null; | ||
64 | + try { | ||
65 | + user = getCurrentUser(); | ||
66 | + } catch (ThingsboardException ex) {} | ||
67 | + if (user == null) { | ||
68 | + response.setStatusCode(HttpStatus.UNAUTHORIZED); | ||
69 | + return false; | ||
70 | + } else { | ||
71 | + attributes.put(WS_SECURITY_USER_ATTRIBUTE, user); | ||
72 | + return true; | ||
73 | + } | ||
74 | + } | ||
75 | + | ||
76 | + @Override | ||
77 | + public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, | ||
78 | + Exception exception) { | ||
79 | + } | ||
80 | + }); | ||
81 | + } | ||
82 | + | ||
83 | + @Bean | ||
84 | + public WebSocketHandler pluginWsHandler() { | ||
85 | + return new PluginWebSocketHandler(); | ||
86 | + } | ||
87 | + | ||
88 | + protected SecurityUser getCurrentUser() throws ThingsboardException { | ||
89 | + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); | ||
90 | + if (authentication != null && authentication.getPrincipal() instanceof SecurityUser) { | ||
91 | + return (SecurityUser) authentication.getPrincipal(); | ||
92 | + } else { | ||
93 | + throw new ThingsboardException("You aren't authorized to perform this operation!", ThingsboardErrorCode.AUTHENTICATION); | ||
94 | + } | ||
95 | + } | ||
96 | +} |
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.controller; | ||
17 | + | ||
18 | +import org.springframework.beans.factory.annotation.Autowired; | ||
19 | +import org.springframework.security.access.prepost.PreAuthorize; | ||
20 | +import org.springframework.web.bind.annotation.*; | ||
21 | +import org.thingsboard.server.common.data.AdminSettings; | ||
22 | +import org.thingsboard.server.dao.settings.AdminSettingsService; | ||
23 | +import org.thingsboard.server.exception.ThingsboardException; | ||
24 | +import org.thingsboard.server.service.mail.MailService; | ||
25 | + | ||
26 | +@RestController | ||
27 | +@RequestMapping("/api/admin") | ||
28 | +public class AdminController extends BaseController { | ||
29 | + | ||
30 | + @Autowired | ||
31 | + private MailService mailService; | ||
32 | + | ||
33 | + @Autowired | ||
34 | + private AdminSettingsService adminSettingsService; | ||
35 | + | ||
36 | + @PreAuthorize("hasAuthority('SYS_ADMIN')") | ||
37 | + @RequestMapping(value = "/settings/{key}", method = RequestMethod.GET) | ||
38 | + @ResponseBody | ||
39 | + public AdminSettings getAdminSettings(@PathVariable("key") String key) throws ThingsboardException { | ||
40 | + try { | ||
41 | + return checkNotNull(adminSettingsService.findAdminSettingsByKey(key)); | ||
42 | + } catch (Exception e) { | ||
43 | + throw handleException(e); | ||
44 | + } | ||
45 | + } | ||
46 | + | ||
47 | + @PreAuthorize("hasAuthority('SYS_ADMIN')") | ||
48 | + @RequestMapping(value = "/settings", method = RequestMethod.POST) | ||
49 | + @ResponseBody | ||
50 | + public AdminSettings saveAdminSettings(@RequestBody AdminSettings adminSettings) throws ThingsboardException { | ||
51 | + try { | ||
52 | + adminSettings = checkNotNull(adminSettingsService.saveAdminSettings(adminSettings)); | ||
53 | + if (adminSettings.getKey().equals("mail")) { | ||
54 | + mailService.updateMailConfiguration(); | ||
55 | + } | ||
56 | + return adminSettings; | ||
57 | + } catch (Exception e) { | ||
58 | + throw handleException(e); | ||
59 | + } | ||
60 | + } | ||
61 | + | ||
62 | + @PreAuthorize("hasAuthority('SYS_ADMIN')") | ||
63 | + @RequestMapping(value = "/settings/testMail", method = RequestMethod.POST) | ||
64 | + public void sendTestMail(@RequestBody AdminSettings adminSettings) throws ThingsboardException { | ||
65 | + try { | ||
66 | + adminSettings = checkNotNull(adminSettings); | ||
67 | + if (adminSettings.getKey().equals("mail")) { | ||
68 | + String email = getCurrentUser().getEmail(); | ||
69 | + mailService.sendTestMail(adminSettings.getJsonValue(), email); | ||
70 | + } | ||
71 | + } catch (Exception e) { | ||
72 | + throw handleException(e); | ||
73 | + } | ||
74 | + } | ||
75 | +} |
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.controller; | ||
17 | + | ||
18 | +import com.fasterxml.jackson.databind.JsonNode; | ||
19 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
20 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
21 | +import lombok.extern.slf4j.Slf4j; | ||
22 | +import org.slf4j.Logger; | ||
23 | +import org.slf4j.LoggerFactory; | ||
24 | +import org.springframework.beans.factory.annotation.Autowired; | ||
25 | +import org.springframework.http.HttpHeaders; | ||
26 | +import org.springframework.http.HttpStatus; | ||
27 | +import org.springframework.http.ResponseEntity; | ||
28 | +import org.springframework.security.access.prepost.PreAuthorize; | ||
29 | +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; | ||
30 | +import org.springframework.web.bind.annotation.*; | ||
31 | +import org.thingsboard.server.common.data.User; | ||
32 | +import org.thingsboard.server.common.data.security.UserCredentials; | ||
33 | +import org.thingsboard.server.dao.user.UserService; | ||
34 | +import org.thingsboard.server.exception.ThingsboardErrorCode; | ||
35 | +import org.thingsboard.server.exception.ThingsboardException; | ||
36 | +import org.thingsboard.server.service.mail.MailService; | ||
37 | +import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository; | ||
38 | +import org.thingsboard.server.service.security.model.SecurityUser; | ||
39 | +import org.thingsboard.server.service.security.model.token.JwtToken; | ||
40 | +import org.thingsboard.server.service.security.model.token.JwtTokenFactory; | ||
41 | + | ||
42 | +import javax.servlet.http.HttpServletRequest; | ||
43 | +import java.net.URI; | ||
44 | +import java.net.URISyntaxException; | ||
45 | + | ||
46 | +@RestController | ||
47 | +@RequestMapping("/api") | ||
48 | +@Slf4j | ||
49 | +public class AuthController extends BaseController { | ||
50 | + | ||
51 | + | ||
52 | + | ||
53 | + @Autowired | ||
54 | + private BCryptPasswordEncoder passwordEncoder; | ||
55 | + | ||
56 | + @Autowired | ||
57 | + private JwtTokenFactory tokenFactory; | ||
58 | + | ||
59 | + @Autowired | ||
60 | + private RefreshTokenRepository refreshTokenRepository; | ||
61 | + | ||
62 | + @Autowired | ||
63 | + private UserService userService; | ||
64 | + | ||
65 | + @Autowired | ||
66 | + private MailService mailService; | ||
67 | + | ||
68 | + @PreAuthorize("isAuthenticated()") | ||
69 | + @RequestMapping(value = "/auth/user", method = RequestMethod.GET) | ||
70 | + public @ResponseBody User getUser() throws ThingsboardException { | ||
71 | + try { | ||
72 | + SecurityUser securityUser = getCurrentUser(); | ||
73 | + return userService.findUserById(securityUser.getId()); | ||
74 | + } catch (Exception e) { | ||
75 | + throw handleException(e); | ||
76 | + } | ||
77 | + } | ||
78 | + | ||
79 | + @PreAuthorize("isAuthenticated()") | ||
80 | + @RequestMapping(value = "/auth/changePassword", method = RequestMethod.POST) | ||
81 | + @ResponseStatus(value = HttpStatus.OK) | ||
82 | + public void changePassword ( | ||
83 | + @RequestParam(value = "currentPassword") String currentPassword, | ||
84 | + @RequestParam(value = "newPassword") String newPassword) throws ThingsboardException { | ||
85 | + try { | ||
86 | + SecurityUser securityUser = getCurrentUser(); | ||
87 | + UserCredentials userCredentials = userService.findUserCredentialsByUserId(securityUser.getId()); | ||
88 | + if (!passwordEncoder.matches(currentPassword, userCredentials.getPassword())) { | ||
89 | + throw new ThingsboardException("Current password doesn't match!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); | ||
90 | + } | ||
91 | + userCredentials.setPassword(passwordEncoder.encode(newPassword)); | ||
92 | + userService.saveUserCredentials(userCredentials); | ||
93 | + } catch (Exception e) { | ||
94 | + throw handleException(e); | ||
95 | + } | ||
96 | + } | ||
97 | + | ||
98 | + @RequestMapping(value = "/noauth/activate", params = { "activateToken" }, method = RequestMethod.GET) | ||
99 | + public ResponseEntity<String> checkActivateToken( | ||
100 | + @RequestParam(value = "activateToken") String activateToken) { | ||
101 | + HttpHeaders headers = new HttpHeaders(); | ||
102 | + HttpStatus responseStatus; | ||
103 | + UserCredentials userCredentials = userService.findUserCredentialsByActivateToken(activateToken); | ||
104 | + if (userCredentials != null) { | ||
105 | + String createPasswordURI = "/login/createPassword"; | ||
106 | + try { | ||
107 | + URI location = new URI(createPasswordURI + "?activateToken=" + activateToken); | ||
108 | + headers.setLocation(location); | ||
109 | + responseStatus = HttpStatus.PERMANENT_REDIRECT; | ||
110 | + } catch (URISyntaxException e) { | ||
111 | + log.error("Unable to create URI with address [{}]", createPasswordURI); | ||
112 | + responseStatus = HttpStatus.BAD_REQUEST; | ||
113 | + } | ||
114 | + } else { | ||
115 | + responseStatus = HttpStatus.CONFLICT; | ||
116 | + } | ||
117 | + return new ResponseEntity<>(headers, responseStatus); | ||
118 | + } | ||
119 | + | ||
120 | + @RequestMapping(value = "/noauth/resetPasswordByEmail", method = RequestMethod.POST) | ||
121 | + @ResponseStatus(value = HttpStatus.OK) | ||
122 | + public void requestResetPasswordByEmail ( | ||
123 | + @RequestParam(value = "email") String email, | ||
124 | + HttpServletRequest request) throws ThingsboardException { | ||
125 | + try { | ||
126 | + UserCredentials userCredentials = userService.requestPasswordReset(email); | ||
127 | + | ||
128 | + String baseUrl = String.format("%s://%s:%d", | ||
129 | + request.getScheme(), | ||
130 | + request.getServerName(), | ||
131 | + request.getServerPort()); | ||
132 | + String resetPasswordUrl = String.format("%s/api/noauth/resetPassword?resetToken=%s", baseUrl, | ||
133 | + userCredentials.getResetToken()); | ||
134 | + | ||
135 | + mailService.sendResetPasswordEmail(resetPasswordUrl, email); | ||
136 | + } catch (Exception e) { | ||
137 | + throw handleException(e); | ||
138 | + } | ||
139 | + } | ||
140 | + | ||
141 | + @RequestMapping(value = "/noauth/resetPassword", params = { "resetToken" }, method = RequestMethod.GET) | ||
142 | + public ResponseEntity<String> checkResetToken( | ||
143 | + @RequestParam(value = "resetToken") String resetToken) { | ||
144 | + HttpHeaders headers = new HttpHeaders(); | ||
145 | + HttpStatus responseStatus; | ||
146 | + String resetPasswordURI = "/login/resetPassword"; | ||
147 | + UserCredentials userCredentials = userService.findUserCredentialsByResetToken(resetToken); | ||
148 | + if (userCredentials != null) { | ||
149 | + try { | ||
150 | + URI location = new URI(resetPasswordURI + "?resetToken=" + resetToken); | ||
151 | + headers.setLocation(location); | ||
152 | + responseStatus = HttpStatus.PERMANENT_REDIRECT; | ||
153 | + } catch (URISyntaxException e) { | ||
154 | + log.error("Unable to create URI with address [{}]", resetPasswordURI); | ||
155 | + responseStatus = HttpStatus.BAD_REQUEST; | ||
156 | + } | ||
157 | + } else { | ||
158 | + responseStatus = HttpStatus.CONFLICT; | ||
159 | + } | ||
160 | + return new ResponseEntity<>(headers, responseStatus); | ||
161 | + } | ||
162 | + | ||
163 | + @RequestMapping(value = "/noauth/activate", method = RequestMethod.POST) | ||
164 | + @ResponseStatus(value = HttpStatus.OK) | ||
165 | + @ResponseBody | ||
166 | + public JsonNode activateUser( | ||
167 | + @RequestParam(value = "activateToken") String activateToken, | ||
168 | + @RequestParam(value = "password") String password, | ||
169 | + HttpServletRequest request) throws ThingsboardException { | ||
170 | + try { | ||
171 | + String encodedPassword = passwordEncoder.encode(password); | ||
172 | + UserCredentials credentials = userService.activateUserCredentials(activateToken, encodedPassword); | ||
173 | + User user = userService.findUserById(credentials.getUserId()); | ||
174 | + SecurityUser securityUser = new SecurityUser(user, credentials.isEnabled()); | ||
175 | + String baseUrl = String.format("%s://%s:%d", | ||
176 | + request.getScheme(), | ||
177 | + request.getServerName(), | ||
178 | + request.getServerPort()); | ||
179 | + String loginUrl = String.format("%s/login", baseUrl); | ||
180 | + String email = user.getEmail(); | ||
181 | + mailService.sendAccountActivatedEmail(loginUrl, email); | ||
182 | + | ||
183 | + JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser); | ||
184 | + JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser); | ||
185 | + | ||
186 | + ObjectMapper objectMapper = new ObjectMapper(); | ||
187 | + ObjectNode tokenObject = objectMapper.createObjectNode(); | ||
188 | + tokenObject.put("token", accessToken.getToken()); | ||
189 | + tokenObject.put("refreshToken", refreshToken.getToken()); | ||
190 | + return tokenObject; | ||
191 | + } catch (Exception e) { | ||
192 | + throw handleException(e); | ||
193 | + } | ||
194 | + } | ||
195 | + | ||
196 | + @RequestMapping(value = "/noauth/resetPassword", method = RequestMethod.POST) | ||
197 | + @ResponseStatus(value = HttpStatus.OK) | ||
198 | + @ResponseBody | ||
199 | + public JsonNode resetPassword( | ||
200 | + @RequestParam(value = "resetToken") String resetToken, | ||
201 | + @RequestParam(value = "password") String password, | ||
202 | + HttpServletRequest request) throws ThingsboardException { | ||
203 | + try { | ||
204 | + UserCredentials userCredentials = userService.findUserCredentialsByResetToken(resetToken); | ||
205 | + if (userCredentials != null) { | ||
206 | + String encodedPassword = passwordEncoder.encode(password); | ||
207 | + userCredentials.setPassword(encodedPassword); | ||
208 | + userCredentials.setResetToken(null); | ||
209 | + userCredentials = userService.saveUserCredentials(userCredentials); | ||
210 | + User user = userService.findUserById(userCredentials.getUserId()); | ||
211 | + SecurityUser securityUser = new SecurityUser(user, userCredentials.isEnabled()); | ||
212 | + String baseUrl = String.format("%s://%s:%d", | ||
213 | + request.getScheme(), | ||
214 | + request.getServerName(), | ||
215 | + request.getServerPort()); | ||
216 | + String loginUrl = String.format("%s/login", baseUrl); | ||
217 | + String email = user.getEmail(); | ||
218 | + mailService.sendPasswordWasResetEmail(loginUrl, email); | ||
219 | + | ||
220 | + JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser); | ||
221 | + JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser); | ||
222 | + | ||
223 | + ObjectMapper objectMapper = new ObjectMapper(); | ||
224 | + ObjectNode tokenObject = objectMapper.createObjectNode(); | ||
225 | + tokenObject.put("token", accessToken.getToken()); | ||
226 | + tokenObject.put("refreshToken", refreshToken.getToken()); | ||
227 | + return tokenObject; | ||
228 | + } else { | ||
229 | + throw new ThingsboardException("Invalid reset token!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); | ||
230 | + } | ||
231 | + } catch (Exception e) { | ||
232 | + throw handleException(e); | ||
233 | + } | ||
234 | + } | ||
235 | + | ||
236 | +} |
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.controller; | ||
17 | + | ||
18 | +import com.fasterxml.jackson.databind.JsonNode; | ||
19 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
20 | +import lombok.extern.slf4j.Slf4j; | ||
21 | +import org.apache.commons.lang3.StringUtils; | ||
22 | +import org.springframework.beans.factory.annotation.Autowired; | ||
23 | +import org.springframework.security.core.Authentication; | ||
24 | +import org.springframework.security.core.context.SecurityContextHolder; | ||
25 | +import org.springframework.web.bind.annotation.ExceptionHandler; | ||
26 | +import org.thingsboard.server.actors.service.ActorService; | ||
27 | +import org.thingsboard.server.common.data.Customer; | ||
28 | +import org.thingsboard.server.common.data.Dashboard; | ||
29 | +import org.thingsboard.server.common.data.Device; | ||
30 | +import org.thingsboard.server.common.data.User; | ||
31 | +import org.thingsboard.server.common.data.id.*; | ||
32 | +import org.thingsboard.server.common.data.page.TextPageLink; | ||
33 | +import org.thingsboard.server.common.data.page.TimePageLink; | ||
34 | +import org.thingsboard.server.common.data.plugin.ComponentDescriptor; | ||
35 | +import org.thingsboard.server.common.data.plugin.ComponentType; | ||
36 | +import org.thingsboard.server.common.data.plugin.PluginMetaData; | ||
37 | +import org.thingsboard.server.common.data.rule.RuleMetaData; | ||
38 | +import org.thingsboard.server.common.data.security.Authority; | ||
39 | +import org.thingsboard.server.common.data.widget.WidgetType; | ||
40 | +import org.thingsboard.server.common.data.widget.WidgetsBundle; | ||
41 | +import org.thingsboard.server.dao.customer.CustomerService; | ||
42 | +import org.thingsboard.server.dao.dashboard.DashboardService; | ||
43 | +import org.thingsboard.server.dao.device.DeviceCredentialsService; | ||
44 | +import org.thingsboard.server.dao.device.DeviceService; | ||
45 | +import org.thingsboard.server.dao.exception.DataValidationException; | ||
46 | +import org.thingsboard.server.dao.exception.IncorrectParameterException; | ||
47 | +import org.thingsboard.server.dao.model.ModelConstants; | ||
48 | +import org.thingsboard.server.dao.plugin.PluginService; | ||
49 | +import org.thingsboard.server.dao.rule.RuleService; | ||
50 | +import org.thingsboard.server.dao.user.UserService; | ||
51 | +import org.thingsboard.server.dao.widget.WidgetTypeService; | ||
52 | +import org.thingsboard.server.dao.widget.WidgetsBundleService; | ||
53 | +import org.thingsboard.server.exception.ThingsboardErrorCode; | ||
54 | +import org.thingsboard.server.exception.ThingsboardErrorResponseHandler; | ||
55 | +import org.thingsboard.server.exception.ThingsboardException; | ||
56 | +import org.thingsboard.server.service.component.ComponentDiscoveryService; | ||
57 | +import org.thingsboard.server.service.security.model.SecurityUser; | ||
58 | + | ||
59 | +import javax.mail.MessagingException; | ||
60 | +import javax.servlet.http.HttpServletResponse; | ||
61 | +import java.util.List; | ||
62 | +import java.util.Optional; | ||
63 | +import java.util.UUID; | ||
64 | + | ||
65 | +import static org.thingsboard.server.dao.service.Validator.validateId; | ||
66 | + | ||
67 | +@Slf4j | ||
68 | +public abstract class BaseController { | ||
69 | + | ||
70 | + @Autowired | ||
71 | + private ThingsboardErrorResponseHandler errorResponseHandler; | ||
72 | + | ||
73 | + @Autowired | ||
74 | + protected CustomerService customerService; | ||
75 | + | ||
76 | + @Autowired | ||
77 | + protected UserService userService; | ||
78 | + | ||
79 | + @Autowired | ||
80 | + protected DeviceService deviceService; | ||
81 | + | ||
82 | + @Autowired | ||
83 | + protected DeviceCredentialsService deviceCredentialsService; | ||
84 | + | ||
85 | + @Autowired | ||
86 | + protected WidgetsBundleService widgetsBundleService; | ||
87 | + | ||
88 | + @Autowired | ||
89 | + protected WidgetTypeService widgetTypeService; | ||
90 | + | ||
91 | + @Autowired | ||
92 | + protected DashboardService dashboardService; | ||
93 | + | ||
94 | + @Autowired | ||
95 | + protected ComponentDiscoveryService componentDescriptorService; | ||
96 | + | ||
97 | + @Autowired | ||
98 | + protected RuleService ruleService; | ||
99 | + | ||
100 | + @Autowired | ||
101 | + protected PluginService pluginService; | ||
102 | + | ||
103 | + @Autowired | ||
104 | + protected ActorService actorService; | ||
105 | + | ||
106 | + | ||
107 | + @ExceptionHandler(ThingsboardException.class) | ||
108 | + public void handleThingsboardException(ThingsboardException ex, HttpServletResponse response) { | ||
109 | + errorResponseHandler.handle(ex, response); | ||
110 | + } | ||
111 | + | ||
112 | + ThingsboardException handleException(Exception exception) { | ||
113 | + return handleException(exception, true); | ||
114 | + } | ||
115 | + | ||
116 | + private ThingsboardException handleException(Exception exception, boolean logException) { | ||
117 | + if (logException) { | ||
118 | + log.error("Error [{}]", exception.getMessage()); | ||
119 | + } | ||
120 | + | ||
121 | + String cause = ""; | ||
122 | + if (exception.getCause() != null) { | ||
123 | + cause = exception.getCause().getClass().getCanonicalName(); | ||
124 | + } | ||
125 | + | ||
126 | + if (exception instanceof ThingsboardException) { | ||
127 | + return (ThingsboardException) exception; | ||
128 | + } else if (exception instanceof IllegalArgumentException || exception instanceof IncorrectParameterException | ||
129 | + || exception instanceof DataValidationException || cause.contains("IncorrectParameterException")) { | ||
130 | + return new ThingsboardException(exception.getMessage(), ThingsboardErrorCode.BAD_REQUEST_PARAMS); | ||
131 | + } else if (exception instanceof MessagingException) { | ||
132 | + return new ThingsboardException("Unable to send mail: " + exception.getMessage(), ThingsboardErrorCode.GENERAL); | ||
133 | + } else { | ||
134 | + return new ThingsboardException(exception.getMessage(), ThingsboardErrorCode.GENERAL); | ||
135 | + } | ||
136 | + } | ||
137 | + | ||
138 | + <T> T checkNotNull(T reference) throws ThingsboardException { | ||
139 | + if (reference == null) { | ||
140 | + throw new ThingsboardException("Requested item wasn't found!", ThingsboardErrorCode.ITEM_NOT_FOUND); | ||
141 | + } | ||
142 | + return reference; | ||
143 | + } | ||
144 | + | ||
145 | + <T> T checkNotNull(Optional<T> reference) throws ThingsboardException { | ||
146 | + if (reference.isPresent()) { | ||
147 | + return reference.get(); | ||
148 | + } else { | ||
149 | + throw new ThingsboardException("Requested item wasn't found!", ThingsboardErrorCode.ITEM_NOT_FOUND); | ||
150 | + } | ||
151 | + } | ||
152 | + | ||
153 | + void checkParameter(String name, String param) throws ThingsboardException { | ||
154 | + if (StringUtils.isEmpty(param)) { | ||
155 | + throw new ThingsboardException("Parameter '" + name + "' can't be empty!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); | ||
156 | + } | ||
157 | + } | ||
158 | + | ||
159 | + UUID toUUID(String id) { | ||
160 | + return UUID.fromString(id); | ||
161 | + } | ||
162 | + | ||
163 | + TimePageLink createPageLink(int limit, Long startTime, Long endTime, boolean ascOrder, String idOffset) { | ||
164 | + UUID idOffsetUuid = null; | ||
165 | + if (StringUtils.isNotEmpty(idOffset)) { | ||
166 | + idOffsetUuid = toUUID(idOffset); | ||
167 | + } | ||
168 | + return new TimePageLink(limit, startTime, endTime, ascOrder, idOffsetUuid); | ||
169 | + } | ||
170 | + | ||
171 | + | ||
172 | + TextPageLink createPageLink(int limit, String textSearch, String idOffset, String textOffset) { | ||
173 | + UUID idOffsetUuid = null; | ||
174 | + if (StringUtils.isNotEmpty(idOffset)) { | ||
175 | + idOffsetUuid = toUUID(idOffset); | ||
176 | + } | ||
177 | + return new TextPageLink(limit, textSearch, idOffsetUuid, textOffset); | ||
178 | + } | ||
179 | + | ||
180 | + protected SecurityUser getCurrentUser() throws ThingsboardException { | ||
181 | + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); | ||
182 | + if (authentication != null && authentication.getPrincipal() instanceof SecurityUser) { | ||
183 | + return (SecurityUser) authentication.getPrincipal(); | ||
184 | + } else { | ||
185 | + throw new ThingsboardException("You aren't authorized to perform this operation!", ThingsboardErrorCode.AUTHENTICATION); | ||
186 | + } | ||
187 | + } | ||
188 | + | ||
189 | + void checkTenantId(TenantId tenantId) throws ThingsboardException { | ||
190 | + validateId(tenantId, "Incorrect tenantId " + tenantId); | ||
191 | + SecurityUser authUser = getCurrentUser(); | ||
192 | + if (authUser.getAuthority() != Authority.SYS_ADMIN && | ||
193 | + (authUser.getTenantId() == null || !authUser.getTenantId().equals(tenantId))) { | ||
194 | + throw new ThingsboardException("You don't have permission to perform this operation!", | ||
195 | + ThingsboardErrorCode.PERMISSION_DENIED); | ||
196 | + } | ||
197 | + } | ||
198 | + | ||
199 | + protected TenantId getTenantId() throws ThingsboardException { | ||
200 | + return getCurrentUser().getTenantId(); | ||
201 | + } | ||
202 | + | ||
203 | + Customer checkCustomerId(CustomerId customerId) throws ThingsboardException { | ||
204 | + try { | ||
205 | + validateId(customerId, "Incorrect customerId " + customerId); | ||
206 | + SecurityUser authUser = getCurrentUser(); | ||
207 | + if (authUser.getAuthority() == Authority.SYS_ADMIN || | ||
208 | + (authUser.getAuthority() != Authority.TENANT_ADMIN && | ||
209 | + (authUser.getCustomerId() == null || !authUser.getCustomerId().equals(customerId)))) { | ||
210 | + throw new ThingsboardException("You don't have permission to perform this operation!", | ||
211 | + ThingsboardErrorCode.PERMISSION_DENIED); | ||
212 | + } | ||
213 | + Customer customer = customerService.findCustomerById(customerId); | ||
214 | + checkCustomer(customer); | ||
215 | + return customer; | ||
216 | + } catch (Exception e) { | ||
217 | + throw handleException(e, false); | ||
218 | + } | ||
219 | + } | ||
220 | + | ||
221 | + private void checkCustomer(Customer customer) throws ThingsboardException { | ||
222 | + checkNotNull(customer); | ||
223 | + checkTenantId(customer.getTenantId()); | ||
224 | + } | ||
225 | + | ||
226 | + User checkUserId(UserId userId) throws ThingsboardException { | ||
227 | + try { | ||
228 | + validateId(userId, "Incorrect userId " + userId); | ||
229 | + User user = userService.findUserById(userId); | ||
230 | + checkUser(user); | ||
231 | + return user; | ||
232 | + } catch (Exception e) { | ||
233 | + throw handleException(e, false); | ||
234 | + } | ||
235 | + } | ||
236 | + | ||
237 | + private void checkUser(User user) throws ThingsboardException { | ||
238 | + checkNotNull(user); | ||
239 | + checkTenantId(user.getTenantId()); | ||
240 | + if (user.getAuthority() == Authority.CUSTOMER_USER) { | ||
241 | + checkCustomerId(user.getCustomerId()); | ||
242 | + } | ||
243 | + } | ||
244 | + | ||
245 | + Device checkDeviceId(DeviceId deviceId) throws ThingsboardException { | ||
246 | + try { | ||
247 | + validateId(deviceId, "Incorrect deviceId " + deviceId); | ||
248 | + Device device = deviceService.findDeviceById(deviceId); | ||
249 | + checkDevice(device); | ||
250 | + return device; | ||
251 | + } catch (Exception e) { | ||
252 | + throw handleException(e, false); | ||
253 | + } | ||
254 | + } | ||
255 | + | ||
256 | + private void checkDevice(Device device) throws ThingsboardException { | ||
257 | + checkNotNull(device); | ||
258 | + checkTenantId(device.getTenantId()); | ||
259 | + if (device.getCustomerId() != null && !device.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) { | ||
260 | + checkCustomerId(device.getCustomerId()); | ||
261 | + } | ||
262 | + } | ||
263 | + | ||
264 | + WidgetsBundle checkWidgetsBundleId(WidgetsBundleId widgetsBundleId, boolean modify) throws ThingsboardException { | ||
265 | + try { | ||
266 | + validateId(widgetsBundleId, "Incorrect widgetsBundleId " + widgetsBundleId); | ||
267 | + WidgetsBundle widgetsBundle = widgetsBundleService.findWidgetsBundleById(widgetsBundleId); | ||
268 | + checkWidgetsBundle(widgetsBundle, modify); | ||
269 | + return widgetsBundle; | ||
270 | + } catch (Exception e) { | ||
271 | + throw handleException(e, false); | ||
272 | + } | ||
273 | + } | ||
274 | + | ||
275 | + private void checkWidgetsBundle(WidgetsBundle widgetsBundle, boolean modify) throws ThingsboardException { | ||
276 | + checkNotNull(widgetsBundle); | ||
277 | + if (widgetsBundle.getTenantId() != null && !widgetsBundle.getTenantId().getId().equals(ModelConstants.NULL_UUID)) { | ||
278 | + checkTenantId(widgetsBundle.getTenantId()); | ||
279 | + } else if (modify && getCurrentUser().getAuthority() != Authority.SYS_ADMIN) { | ||
280 | + throw new ThingsboardException("You don't have permission to perform this operation!", | ||
281 | + ThingsboardErrorCode.PERMISSION_DENIED); | ||
282 | + } | ||
283 | + } | ||
284 | + | ||
285 | + WidgetType checkWidgetTypeId(WidgetTypeId widgetTypeId, boolean modify) throws ThingsboardException { | ||
286 | + try { | ||
287 | + validateId(widgetTypeId, "Incorrect widgetTypeId " + widgetTypeId); | ||
288 | + WidgetType widgetType = widgetTypeService.findWidgetTypeById(widgetTypeId); | ||
289 | + checkWidgetType(widgetType, modify); | ||
290 | + return widgetType; | ||
291 | + } catch (Exception e) { | ||
292 | + throw handleException(e, false); | ||
293 | + } | ||
294 | + } | ||
295 | + | ||
296 | + void checkWidgetType(WidgetType widgetType, boolean modify) throws ThingsboardException { | ||
297 | + checkNotNull(widgetType); | ||
298 | + if (widgetType.getTenantId() != null && !widgetType.getTenantId().getId().equals(ModelConstants.NULL_UUID)) { | ||
299 | + checkTenantId(widgetType.getTenantId()); | ||
300 | + } else if (modify && getCurrentUser().getAuthority() != Authority.SYS_ADMIN) { | ||
301 | + throw new ThingsboardException("You don't have permission to perform this operation!", | ||
302 | + ThingsboardErrorCode.PERMISSION_DENIED); | ||
303 | + } | ||
304 | + } | ||
305 | + | ||
306 | + Dashboard checkDashboardId(DashboardId dashboardId) throws ThingsboardException { | ||
307 | + try { | ||
308 | + validateId(dashboardId, "Incorrect dashboardId " + dashboardId); | ||
309 | + Dashboard dashboard = dashboardService.findDashboardById(dashboardId); | ||
310 | + checkDashboard(dashboard); | ||
311 | + return dashboard; | ||
312 | + } catch (Exception e) { | ||
313 | + throw handleException(e, false); | ||
314 | + } | ||
315 | + } | ||
316 | + | ||
317 | + private void checkDashboard(Dashboard dashboard) throws ThingsboardException { | ||
318 | + checkNotNull(dashboard); | ||
319 | + checkTenantId(dashboard.getTenantId()); | ||
320 | + if (dashboard.getCustomerId() != null && !dashboard.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) { | ||
321 | + checkCustomerId(dashboard.getCustomerId()); | ||
322 | + } | ||
323 | + } | ||
324 | + | ||
325 | + ComponentDescriptor checkComponentDescriptorByClazz(String clazz) throws ThingsboardException { | ||
326 | + try { | ||
327 | + log.debug("[{}] Lookup component descriptor", clazz); | ||
328 | + ComponentDescriptor componentDescriptor = checkNotNull(componentDescriptorService.getComponent(clazz)); | ||
329 | + return componentDescriptor; | ||
330 | + } catch (Exception e) { | ||
331 | + throw handleException(e, false); | ||
332 | + } | ||
333 | + } | ||
334 | + | ||
335 | + List<ComponentDescriptor> checkComponentDescriptorsByType(ComponentType type) throws ThingsboardException { | ||
336 | + try { | ||
337 | + log.debug("[{}] Lookup component descriptors", type); | ||
338 | + return componentDescriptorService.getComponents(type); | ||
339 | + } catch (Exception e) { | ||
340 | + throw handleException(e, false); | ||
341 | + } | ||
342 | + } | ||
343 | + | ||
344 | + List<ComponentDescriptor> checkPluginActionsByPluginClazz(String pluginClazz) throws ThingsboardException { | ||
345 | + try { | ||
346 | + checkComponentDescriptorByClazz(pluginClazz); | ||
347 | + log.debug("[{}] Lookup plugin actions", pluginClazz); | ||
348 | + return componentDescriptorService.getPluginActions(pluginClazz); | ||
349 | + } catch (Exception e) { | ||
350 | + throw handleException(e, false); | ||
351 | + } | ||
352 | + } | ||
353 | + | ||
354 | + protected PluginMetaData checkPlugin(PluginMetaData plugin) throws ThingsboardException { | ||
355 | + checkNotNull(plugin); | ||
356 | + SecurityUser authUser = getCurrentUser(); | ||
357 | + TenantId tenantId = plugin.getTenantId(); | ||
358 | + validateId(tenantId, "Incorrect tenantId " + tenantId); | ||
359 | + if (authUser.getAuthority() != Authority.SYS_ADMIN) { | ||
360 | + if (authUser.getTenantId() == null || | ||
361 | + !tenantId.getId().equals(ModelConstants.NULL_UUID) && !authUser.getTenantId().equals(tenantId)) { | ||
362 | + throw new ThingsboardException("You don't have permission to perform this operation!", | ||
363 | + ThingsboardErrorCode.PERMISSION_DENIED); | ||
364 | + | ||
365 | + } else if (tenantId.getId().equals(ModelConstants.NULL_UUID)) { | ||
366 | + plugin.setConfiguration(null); | ||
367 | + } | ||
368 | + } | ||
369 | + return plugin; | ||
370 | + } | ||
371 | + | ||
372 | + protected RuleMetaData checkRule(RuleMetaData rule) throws ThingsboardException { | ||
373 | + checkNotNull(rule); | ||
374 | + checkTenantId(rule.getTenantId()); | ||
375 | + return rule; | ||
376 | + } | ||
377 | +} |
application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.controller; | ||
17 | + | ||
18 | +import org.springframework.security.access.prepost.PreAuthorize; | ||
19 | +import org.springframework.web.bind.annotation.*; | ||
20 | +import org.thingsboard.server.common.data.plugin.ComponentDescriptor; | ||
21 | +import org.thingsboard.server.common.data.plugin.ComponentType; | ||
22 | +import org.thingsboard.server.exception.ThingsboardException; | ||
23 | + | ||
24 | +import java.util.List; | ||
25 | + | ||
26 | +@RestController | ||
27 | +@RequestMapping("/api") | ||
28 | +public class ComponentDescriptorController extends BaseController { | ||
29 | + | ||
30 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN')") | ||
31 | + @RequestMapping(value = "/component/{componentDescriptorClazz:.+}", method = RequestMethod.GET) | ||
32 | + @ResponseBody | ||
33 | + public ComponentDescriptor getComponentDescriptorByClazz(@PathVariable("componentDescriptorClazz") String strComponentDescriptorClazz) throws ThingsboardException { | ||
34 | + checkParameter("strComponentDescriptorClazz", strComponentDescriptorClazz); | ||
35 | + try { | ||
36 | + return checkComponentDescriptorByClazz(strComponentDescriptorClazz); | ||
37 | + } catch (Exception e) { | ||
38 | + throw handleException(e); | ||
39 | + } | ||
40 | + } | ||
41 | + | ||
42 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN')") | ||
43 | + @RequestMapping(value = "/components/{componentType}", method = RequestMethod.GET) | ||
44 | + @ResponseBody | ||
45 | + public List<ComponentDescriptor> getComponentDescriptorsByType(@PathVariable("componentType") String strComponentType) throws ThingsboardException { | ||
46 | + checkParameter("componentType", strComponentType); | ||
47 | + try { | ||
48 | + return checkComponentDescriptorsByType(ComponentType.valueOf(strComponentType)); | ||
49 | + } catch (Exception e) { | ||
50 | + throw handleException(e); | ||
51 | + } | ||
52 | + } | ||
53 | + | ||
54 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN')") | ||
55 | + @RequestMapping(value = "/components/actions/{pluginClazz:.+}", method = RequestMethod.GET) | ||
56 | + @ResponseBody | ||
57 | + public List<ComponentDescriptor> getPluginActionsByPluginClazz(@PathVariable("pluginClazz") String pluginClazz) throws ThingsboardException { | ||
58 | + checkParameter("pluginClazz", pluginClazz); | ||
59 | + try { | ||
60 | + return checkPluginActionsByPluginClazz(pluginClazz); | ||
61 | + } catch (Exception e) { | ||
62 | + throw handleException(e); | ||
63 | + } | ||
64 | + } | ||
65 | + | ||
66 | +} |
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.controller; | ||
17 | + | ||
18 | +import org.springframework.http.HttpStatus; | ||
19 | +import org.springframework.security.access.prepost.PreAuthorize; | ||
20 | +import org.springframework.web.bind.annotation.*; | ||
21 | +import org.thingsboard.server.common.data.Customer; | ||
22 | +import org.thingsboard.server.common.data.id.CustomerId; | ||
23 | +import org.thingsboard.server.common.data.id.TenantId; | ||
24 | +import org.thingsboard.server.common.data.page.TextPageData; | ||
25 | +import org.thingsboard.server.common.data.page.TextPageLink; | ||
26 | +import org.thingsboard.server.exception.ThingsboardException; | ||
27 | + | ||
28 | +@RestController | ||
29 | +@RequestMapping("/api") | ||
30 | +public class CustomerController extends BaseController { | ||
31 | + | ||
32 | + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | ||
33 | + @RequestMapping(value = "/customer/{customerId}", method = RequestMethod.GET) | ||
34 | + @ResponseBody | ||
35 | + public Customer getCustomerById(@PathVariable("customerId") String strCustomerId) throws ThingsboardException { | ||
36 | + checkParameter("customerId", strCustomerId); | ||
37 | + try { | ||
38 | + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); | ||
39 | + return checkCustomerId(customerId); | ||
40 | + } catch (Exception e) { | ||
41 | + throw handleException(e); | ||
42 | + } | ||
43 | + } | ||
44 | + | ||
45 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | ||
46 | + @RequestMapping(value = "/customer", method = RequestMethod.POST) | ||
47 | + @ResponseBody | ||
48 | + public Customer saveCustomer(@RequestBody Customer customer) throws ThingsboardException { | ||
49 | + try { | ||
50 | + customer.setTenantId(getCurrentUser().getTenantId()); | ||
51 | + return checkNotNull(customerService.saveCustomer(customer)); | ||
52 | + } catch (Exception e) { | ||
53 | + throw handleException(e); | ||
54 | + } | ||
55 | + } | ||
56 | + | ||
57 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | ||
58 | + @RequestMapping(value = "/customer/{customerId}", method = RequestMethod.DELETE) | ||
59 | + @ResponseStatus(value = HttpStatus.OK) | ||
60 | + public void deleteCustomer(@PathVariable("customerId") String strCustomerId) throws ThingsboardException { | ||
61 | + checkParameter("customerId", strCustomerId); | ||
62 | + try { | ||
63 | + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); | ||
64 | + checkCustomerId(customerId); | ||
65 | + customerService.deleteCustomer(customerId); | ||
66 | + } catch (Exception e) { | ||
67 | + throw handleException(e); | ||
68 | + } | ||
69 | + } | ||
70 | + | ||
71 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | ||
72 | + @RequestMapping(value = "/customers", params = { "limit" }, method = RequestMethod.GET) | ||
73 | + @ResponseBody | ||
74 | + public TextPageData<Customer> getCustomers(@RequestParam int limit, | ||
75 | + @RequestParam(required = false) String textSearch, | ||
76 | + @RequestParam(required = false) String idOffset, | ||
77 | + @RequestParam(required = false) String textOffset) throws ThingsboardException { | ||
78 | + try { | ||
79 | + TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); | ||
80 | + TenantId tenantId = getCurrentUser().getTenantId(); | ||
81 | + return checkNotNull(customerService.findCustomersByTenantId(tenantId, pageLink)); | ||
82 | + } catch (Exception e) { | ||
83 | + throw handleException(e); | ||
84 | + } | ||
85 | + } | ||
86 | + | ||
87 | +} |
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.controller; | ||
17 | + | ||
18 | +import org.springframework.http.HttpStatus; | ||
19 | +import org.springframework.security.access.prepost.PreAuthorize; | ||
20 | +import org.springframework.web.bind.annotation.*; | ||
21 | +import org.thingsboard.server.common.data.Dashboard; | ||
22 | +import org.thingsboard.server.common.data.id.CustomerId; | ||
23 | +import org.thingsboard.server.common.data.id.DashboardId; | ||
24 | +import org.thingsboard.server.common.data.id.TenantId; | ||
25 | +import org.thingsboard.server.common.data.page.TextPageData; | ||
26 | +import org.thingsboard.server.common.data.page.TextPageLink; | ||
27 | +import org.thingsboard.server.dao.exception.IncorrectParameterException; | ||
28 | +import org.thingsboard.server.dao.model.ModelConstants; | ||
29 | +import org.thingsboard.server.exception.ThingsboardException; | ||
30 | + | ||
31 | +@RestController | ||
32 | +@RequestMapping("/api") | ||
33 | +public class DashboardController extends BaseController { | ||
34 | + | ||
35 | + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | ||
36 | + @RequestMapping(value = "/dashboard/{dashboardId}", method = RequestMethod.GET) | ||
37 | + @ResponseBody | ||
38 | + public Dashboard getDashboardById(@PathVariable("dashboardId") String strDashboardId) throws ThingsboardException { | ||
39 | + checkParameter("dashboardId", strDashboardId); | ||
40 | + try { | ||
41 | + DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); | ||
42 | + return checkDashboardId(dashboardId); | ||
43 | + } catch (Exception e) { | ||
44 | + throw handleException(e); | ||
45 | + } | ||
46 | + } | ||
47 | + | ||
48 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | ||
49 | + @RequestMapping(value = "/dashboard", method = RequestMethod.POST) | ||
50 | + @ResponseBody | ||
51 | + public Dashboard saveDashboard(@RequestBody Dashboard dashboard) throws ThingsboardException { | ||
52 | + try { | ||
53 | + dashboard.setTenantId(getCurrentUser().getTenantId()); | ||
54 | + return checkNotNull(dashboardService.saveDashboard(dashboard)); | ||
55 | + } catch (Exception e) { | ||
56 | + throw handleException(e); | ||
57 | + } | ||
58 | + } | ||
59 | + | ||
60 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | ||
61 | + @RequestMapping(value = "/dashboard/{dashboardId}", method = RequestMethod.DELETE) | ||
62 | + @ResponseStatus(value = HttpStatus.OK) | ||
63 | + public void deleteDashboard(@PathVariable("dashboardId") String strDashboardId) throws ThingsboardException { | ||
64 | + checkParameter("dashboardId", strDashboardId); | ||
65 | + try { | ||
66 | + DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); | ||
67 | + checkDashboardId(dashboardId); | ||
68 | + dashboardService.deleteDashboard(dashboardId); | ||
69 | + } catch (Exception e) { | ||
70 | + throw handleException(e); | ||
71 | + } | ||
72 | + } | ||
73 | + | ||
74 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | ||
75 | + @RequestMapping(value = "/customer/{customerId}/dashboard/{dashboardId}", method = RequestMethod.POST) | ||
76 | + @ResponseBody | ||
77 | + public Dashboard assignDashboardToCustomer(@PathVariable("customerId") String strCustomerId, | ||
78 | + @PathVariable("dashboardId") String strDashboardId) throws ThingsboardException { | ||
79 | + checkParameter("customerId", strCustomerId); | ||
80 | + checkParameter("dashboardId", strDashboardId); | ||
81 | + try { | ||
82 | + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); | ||
83 | + checkCustomerId(customerId); | ||
84 | + | ||
85 | + DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); | ||
86 | + checkDashboardId(dashboardId); | ||
87 | + | ||
88 | + return checkNotNull(dashboardService.assignDashboardToCustomer(dashboardId, customerId)); | ||
89 | + } catch (Exception e) { | ||
90 | + throw handleException(e); | ||
91 | + } | ||
92 | + } | ||
93 | + | ||
94 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | ||
95 | + @RequestMapping(value = "/customer/dashboard/{dashboardId}", method = RequestMethod.DELETE) | ||
96 | + @ResponseBody | ||
97 | + public Dashboard unassignDashboardFromCustomer(@PathVariable("dashboardId") String strDashboardId) throws ThingsboardException { | ||
98 | + checkParameter("dashboardId", strDashboardId); | ||
99 | + try { | ||
100 | + DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); | ||
101 | + Dashboard dashboard = checkDashboardId(dashboardId); | ||
102 | + if (dashboard.getCustomerId() == null || dashboard.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) { | ||
103 | + throw new IncorrectParameterException("Dashboard isn't assigned to any customer!"); | ||
104 | + } | ||
105 | + return checkNotNull(dashboardService.unassignDashboardFromCustomer(dashboardId)); | ||
106 | + } catch (Exception e) { | ||
107 | + throw handleException(e); | ||
108 | + } | ||
109 | + } | ||
110 | + | ||
111 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | ||
112 | + @RequestMapping(value = "/tenant/dashboards", params = { "limit" }, method = RequestMethod.GET) | ||
113 | + @ResponseBody | ||
114 | + public TextPageData<Dashboard> getTenantDashboards( | ||
115 | + @RequestParam int limit, | ||
116 | + @RequestParam(required = false) String textSearch, | ||
117 | + @RequestParam(required = false) String idOffset, | ||
118 | + @RequestParam(required = false) String textOffset) throws ThingsboardException { | ||
119 | + try { | ||
120 | + TenantId tenantId = getCurrentUser().getTenantId(); | ||
121 | + TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); | ||
122 | + return checkNotNull(dashboardService.findDashboardsByTenantId(tenantId, pageLink)); | ||
123 | + } catch (Exception e) { | ||
124 | + throw handleException(e); | ||
125 | + } | ||
126 | + } | ||
127 | + | ||
128 | + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | ||
129 | + @RequestMapping(value = "/customer/{customerId}/dashboards", params = { "limit" }, method = RequestMethod.GET) | ||
130 | + @ResponseBody | ||
131 | + public TextPageData<Dashboard> getCustomerDashboards( | ||
132 | + @PathVariable("customerId") String strCustomerId, | ||
133 | + @RequestParam int limit, | ||
134 | + @RequestParam(required = false) String textSearch, | ||
135 | + @RequestParam(required = false) String idOffset, | ||
136 | + @RequestParam(required = false) String textOffset) throws ThingsboardException { | ||
137 | + checkParameter("customerId", strCustomerId); | ||
138 | + try { | ||
139 | + TenantId tenantId = getCurrentUser().getTenantId(); | ||
140 | + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); | ||
141 | + checkCustomerId(customerId); | ||
142 | + TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); | ||
143 | + return checkNotNull(dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink)); | ||
144 | + } catch (Exception e) { | ||
145 | + throw handleException(e); | ||
146 | + } | ||
147 | + } | ||
148 | +} |
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.controller; | ||
17 | + | ||
18 | +import org.springframework.http.HttpStatus; | ||
19 | +import org.springframework.security.access.prepost.PreAuthorize; | ||
20 | +import org.springframework.web.bind.annotation.*; | ||
21 | +import org.thingsboard.server.common.data.Device; | ||
22 | +import org.thingsboard.server.common.data.id.CustomerId; | ||
23 | +import org.thingsboard.server.common.data.id.DeviceId; | ||
24 | +import org.thingsboard.server.common.data.id.TenantId; | ||
25 | +import org.thingsboard.server.common.data.page.TextPageData; | ||
26 | +import org.thingsboard.server.common.data.page.TextPageLink; | ||
27 | +import org.thingsboard.server.common.data.security.DeviceCredentials; | ||
28 | +import org.thingsboard.server.dao.exception.IncorrectParameterException; | ||
29 | +import org.thingsboard.server.dao.model.ModelConstants; | ||
30 | +import org.thingsboard.server.exception.ThingsboardException; | ||
31 | + | ||
32 | +@RestController | ||
33 | +@RequestMapping("/api") | ||
34 | +public class DeviceController extends BaseController { | ||
35 | + | ||
36 | + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | ||
37 | + @RequestMapping(value = "/device/{deviceId}", method = RequestMethod.GET) | ||
38 | + @ResponseBody | ||
39 | + public Device getDeviceById(@PathVariable("deviceId") String strDeviceId) throws ThingsboardException { | ||
40 | + checkParameter("deviceId", strDeviceId); | ||
41 | + try { | ||
42 | + DeviceId deviceId = new DeviceId(toUUID(strDeviceId)); | ||
43 | + return checkDeviceId(deviceId); | ||
44 | + } catch (Exception e) { | ||
45 | + throw handleException(e); | ||
46 | + } | ||
47 | + } | ||
48 | + | ||
49 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | ||
50 | + @RequestMapping(value = "/device", method = RequestMethod.POST) | ||
51 | + @ResponseBody | ||
52 | + public Device saveDevice(@RequestBody Device device) throws ThingsboardException { | ||
53 | + try { | ||
54 | + device.setTenantId(getCurrentUser().getTenantId()); | ||
55 | + return checkNotNull(deviceService.saveDevice(device)); | ||
56 | + } catch (Exception e) { | ||
57 | + throw handleException(e); | ||
58 | + } | ||
59 | + } | ||
60 | + | ||
61 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | ||
62 | + @RequestMapping(value = "/device/{deviceId}", method = RequestMethod.DELETE) | ||
63 | + @ResponseStatus(value = HttpStatus.OK) | ||
64 | + public void deleteDevice(@PathVariable("deviceId") String strDeviceId) throws ThingsboardException { | ||
65 | + checkParameter("deviceId", strDeviceId); | ||
66 | + try { | ||
67 | + DeviceId deviceId = new DeviceId(toUUID(strDeviceId)); | ||
68 | + checkDeviceId(deviceId); | ||
69 | + deviceService.deleteDevice(deviceId); | ||
70 | + } catch (Exception e) { | ||
71 | + throw handleException(e); | ||
72 | + } | ||
73 | + } | ||
74 | + | ||
75 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | ||
76 | + @RequestMapping(value = "/customer/{customerId}/device/{deviceId}", method = RequestMethod.POST) | ||
77 | + @ResponseBody | ||
78 | + public Device assignDeviceToCustomer(@PathVariable("customerId") String strCustomerId, | ||
79 | + @PathVariable("deviceId") String strDeviceId) throws ThingsboardException { | ||
80 | + checkParameter("customerId", strCustomerId); | ||
81 | + checkParameter("deviceId", strDeviceId); | ||
82 | + try { | ||
83 | + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); | ||
84 | + checkCustomerId(customerId); | ||
85 | + | ||
86 | + DeviceId deviceId = new DeviceId(toUUID(strDeviceId)); | ||
87 | + checkDeviceId(deviceId); | ||
88 | + | ||
89 | + return checkNotNull(deviceService.assignDeviceToCustomer(deviceId, customerId)); | ||
90 | + } catch (Exception e) { | ||
91 | + throw handleException(e); | ||
92 | + } | ||
93 | + } | ||
94 | + | ||
95 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | ||
96 | + @RequestMapping(value = "/customer/device/{deviceId}", method = RequestMethod.DELETE) | ||
97 | + @ResponseBody | ||
98 | + public Device unassignDeviceFromCustomer(@PathVariable("deviceId") String strDeviceId) throws ThingsboardException { | ||
99 | + checkParameter("deviceId", strDeviceId); | ||
100 | + try { | ||
101 | + DeviceId deviceId = new DeviceId(toUUID(strDeviceId)); | ||
102 | + Device device = checkDeviceId(deviceId); | ||
103 | + if (device.getCustomerId() == null || device.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) { | ||
104 | + throw new IncorrectParameterException("Device isn't assigned to any customer!"); | ||
105 | + } | ||
106 | + return checkNotNull(deviceService.unassignDeviceFromCustomer(deviceId)); | ||
107 | + } catch (Exception e) { | ||
108 | + throw handleException(e); | ||
109 | + } | ||
110 | + } | ||
111 | + | ||
112 | + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | ||
113 | + @RequestMapping(value = "/device/{deviceId}/credentials", method = RequestMethod.GET) | ||
114 | + @ResponseBody | ||
115 | + public DeviceCredentials getDeviceCredentialsByDeviceId(@PathVariable("deviceId") String strDeviceId) throws ThingsboardException { | ||
116 | + checkParameter("deviceId", strDeviceId); | ||
117 | + try { | ||
118 | + DeviceId deviceId = new DeviceId(toUUID(strDeviceId)); | ||
119 | + checkDeviceId(deviceId); | ||
120 | + return checkNotNull(deviceCredentialsService.findDeviceCredentialsByDeviceId(deviceId)); | ||
121 | + } catch (Exception e) { | ||
122 | + throw handleException(e); | ||
123 | + } | ||
124 | + } | ||
125 | + | ||
126 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | ||
127 | + @RequestMapping(value = "/device/credentials", method = RequestMethod.POST) | ||
128 | + @ResponseBody | ||
129 | + public DeviceCredentials saveDeviceCredentials(@RequestBody DeviceCredentials deviceCredentials) throws ThingsboardException { | ||
130 | + checkNotNull(deviceCredentials); | ||
131 | + try { | ||
132 | + checkDeviceId(deviceCredentials.getDeviceId()); | ||
133 | + return checkNotNull(deviceCredentialsService.updateDeviceCredentials(deviceCredentials)); | ||
134 | + } catch (Exception e) { | ||
135 | + throw handleException(e); | ||
136 | + } | ||
137 | + } | ||
138 | + | ||
139 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | ||
140 | + @RequestMapping(value = "/tenant/devices", params = { "limit" }, method = RequestMethod.GET) | ||
141 | + @ResponseBody | ||
142 | + public TextPageData<Device> getTenantDevices( | ||
143 | + @RequestParam int limit, | ||
144 | + @RequestParam(required = false) String textSearch, | ||
145 | + @RequestParam(required = false) String idOffset, | ||
146 | + @RequestParam(required = false) String textOffset) throws ThingsboardException { | ||
147 | + try { | ||
148 | + TenantId tenantId = getCurrentUser().getTenantId(); | ||
149 | + TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); | ||
150 | + return checkNotNull(deviceService.findDevicesByTenantId(tenantId, pageLink)); | ||
151 | + } catch (Exception e) { | ||
152 | + throw handleException(e); | ||
153 | + } | ||
154 | + } | ||
155 | + | ||
156 | + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | ||
157 | + @RequestMapping(value = "/customer/{customerId}/devices", params = { "limit" }, method = RequestMethod.GET) | ||
158 | + @ResponseBody | ||
159 | + public TextPageData<Device> getCustomerDevices( | ||
160 | + @PathVariable("customerId") String strCustomerId, | ||
161 | + @RequestParam int limit, | ||
162 | + @RequestParam(required = false) String textSearch, | ||
163 | + @RequestParam(required = false) String idOffset, | ||
164 | + @RequestParam(required = false) String textOffset) throws ThingsboardException { | ||
165 | + checkParameter("customerId", strCustomerId); | ||
166 | + try { | ||
167 | + TenantId tenantId = getCurrentUser().getTenantId(); | ||
168 | + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); | ||
169 | + checkCustomerId(customerId); | ||
170 | + TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); | ||
171 | + return checkNotNull(deviceService.findDevicesByTenantIdAndCustomerId(tenantId, customerId, pageLink)); | ||
172 | + } catch (Exception e) { | ||
173 | + throw handleException(e); | ||
174 | + } | ||
175 | + } | ||
176 | +} |
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.controller; | ||
17 | + | ||
18 | +import org.springframework.beans.factory.annotation.Autowired; | ||
19 | +import org.springframework.security.access.prepost.PreAuthorize; | ||
20 | +import org.springframework.web.bind.annotation.*; | ||
21 | +import org.thingsboard.server.common.data.EntityType; | ||
22 | +import org.thingsboard.server.common.data.Event; | ||
23 | +import org.thingsboard.server.common.data.id.*; | ||
24 | +import org.thingsboard.server.common.data.page.TimePageData; | ||
25 | +import org.thingsboard.server.common.data.page.TimePageLink; | ||
26 | +import org.thingsboard.server.dao.event.EventService; | ||
27 | +import org.thingsboard.server.dao.model.ModelConstants; | ||
28 | +import org.thingsboard.server.exception.ThingsboardErrorCode; | ||
29 | +import org.thingsboard.server.exception.ThingsboardException; | ||
30 | + | ||
31 | +@RestController | ||
32 | +@RequestMapping("/api") | ||
33 | +public class EventController extends BaseController { | ||
34 | + | ||
35 | + @Autowired | ||
36 | + private EventService eventService; | ||
37 | + | ||
38 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") | ||
39 | + @RequestMapping(value = "/events/{entityType}/{entityId}/{eventType}", method = RequestMethod.GET) | ||
40 | + @ResponseBody | ||
41 | + public TimePageData<Event> getEvents( | ||
42 | + @PathVariable("entityType") String strEntityType, | ||
43 | + @PathVariable("entityId") String strEntityId, | ||
44 | + @PathVariable("eventType") String eventType, | ||
45 | + @RequestParam("tenantId") String strTenantId, | ||
46 | + @RequestParam int limit, | ||
47 | + @RequestParam(required = false) Long startTime, | ||
48 | + @RequestParam(required = false) Long endTime, | ||
49 | + @RequestParam(required = false, defaultValue = "false") boolean ascOrder, | ||
50 | + @RequestParam(required = false) String offset | ||
51 | + ) throws ThingsboardException { | ||
52 | + checkParameter("EntityId", strEntityId); | ||
53 | + checkParameter("EntityType", strEntityType); | ||
54 | + try { | ||
55 | + TenantId tenantId = new TenantId(toUUID(strTenantId)); | ||
56 | + if (!tenantId.getId().equals(ModelConstants.NULL_UUID) && | ||
57 | + !tenantId.equals(getCurrentUser().getTenantId())) { | ||
58 | + throw new ThingsboardException("You don't have permission to perform this operation!", | ||
59 | + ThingsboardErrorCode.PERMISSION_DENIED); | ||
60 | + } | ||
61 | + TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset); | ||
62 | + return checkNotNull(eventService.findEvents(tenantId, getEntityId(strEntityType, strEntityId), eventType, pageLink)); | ||
63 | + } catch (Exception e) { | ||
64 | + throw handleException(e); | ||
65 | + } | ||
66 | + } | ||
67 | + | ||
68 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") | ||
69 | + @RequestMapping(value = "/events/{entityType}/{entityId}", method = RequestMethod.GET) | ||
70 | + @ResponseBody | ||
71 | + public TimePageData<Event> getEvents( | ||
72 | + @PathVariable("entityType") String strEntityType, | ||
73 | + @PathVariable("entityId") String strEntityId, | ||
74 | + @RequestParam("tenantId") String strTenantId, | ||
75 | + @RequestParam int limit, | ||
76 | + @RequestParam(required = false) Long startTime, | ||
77 | + @RequestParam(required = false) Long endTime, | ||
78 | + @RequestParam(required = false, defaultValue = "false") boolean ascOrder, | ||
79 | + @RequestParam(required = false) String offset | ||
80 | + ) throws ThingsboardException { | ||
81 | + checkParameter("EntityId", strEntityId); | ||
82 | + checkParameter("EntityType", strEntityType); | ||
83 | + try { | ||
84 | + TenantId tenantId = new TenantId(toUUID(strTenantId)); | ||
85 | + if (!tenantId.getId().equals(ModelConstants.NULL_UUID) && | ||
86 | + !tenantId.equals(getCurrentUser().getTenantId())) { | ||
87 | + throw new ThingsboardException("You don't have permission to perform this operation!", | ||
88 | + ThingsboardErrorCode.PERMISSION_DENIED); | ||
89 | + } | ||
90 | + TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset); | ||
91 | + return checkNotNull(eventService.findEvents(tenantId, getEntityId(strEntityType, strEntityId), pageLink)); | ||
92 | + } catch (Exception e) { | ||
93 | + throw handleException(e); | ||
94 | + } | ||
95 | + } | ||
96 | + | ||
97 | + | ||
98 | + private EntityId getEntityId(String strEntityType, String strEntityId) throws ThingsboardException { | ||
99 | + EntityId entityId; | ||
100 | + EntityType entityType = EntityType.valueOf(strEntityType); | ||
101 | + switch (entityType) { | ||
102 | + case RULE: | ||
103 | + entityId = new RuleId(toUUID(strEntityId)); | ||
104 | + break; | ||
105 | + case PLUGIN: | ||
106 | + entityId = new PluginId(toUUID(strEntityId)); | ||
107 | + break; | ||
108 | + case DEVICE: | ||
109 | + entityId = new DeviceId(toUUID(strEntityId)); | ||
110 | + break; | ||
111 | + default: | ||
112 | + throw new ThingsboardException("EntityType ['" + entityType + "'] is incorrect!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); | ||
113 | + } | ||
114 | + return entityId; | ||
115 | + } | ||
116 | +} |
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.controller; | ||
17 | + | ||
18 | +import org.springframework.http.HttpStatus; | ||
19 | +import org.springframework.security.access.prepost.PreAuthorize; | ||
20 | +import org.springframework.web.bind.annotation.*; | ||
21 | +import org.thingsboard.server.common.data.id.PluginId; | ||
22 | +import org.thingsboard.server.common.data.id.TenantId; | ||
23 | +import org.thingsboard.server.common.data.page.TextPageData; | ||
24 | +import org.thingsboard.server.common.data.page.TextPageLink; | ||
25 | +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; | ||
26 | +import org.thingsboard.server.common.data.plugin.PluginMetaData; | ||
27 | +import org.thingsboard.server.common.data.security.Authority; | ||
28 | +import org.thingsboard.server.common.data.widget.WidgetsBundle; | ||
29 | +import org.thingsboard.server.dao.model.ModelConstants; | ||
30 | +import org.thingsboard.server.exception.ThingsboardException; | ||
31 | + | ||
32 | +import java.util.List; | ||
33 | + | ||
34 | +@RestController | ||
35 | +@RequestMapping("/api") | ||
36 | +public class PluginController extends BaseController { | ||
37 | + | ||
38 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") | ||
39 | + @RequestMapping(value = "/plugin/{pluginId}", method = RequestMethod.GET) | ||
40 | + @ResponseBody | ||
41 | + public PluginMetaData getPluginById(@PathVariable("pluginId") String strPluginId) throws ThingsboardException { | ||
42 | + checkParameter("pluginId", strPluginId); | ||
43 | + try { | ||
44 | + PluginId pluginId = new PluginId(toUUID(strPluginId)); | ||
45 | + return checkPlugin(pluginService.findPluginById(pluginId)); | ||
46 | + } catch (Exception e) { | ||
47 | + throw handleException(e); | ||
48 | + } | ||
49 | + } | ||
50 | + | ||
51 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") | ||
52 | + @RequestMapping(value = "/plugin/token/{pluginToken}", method = RequestMethod.GET) | ||
53 | + @ResponseBody | ||
54 | + public PluginMetaData getPluginByToken(@PathVariable("pluginToken") String pluginToken) throws ThingsboardException { | ||
55 | + checkParameter("pluginToken", pluginToken); | ||
56 | + try { | ||
57 | + return checkPlugin(pluginService.findPluginByApiToken(pluginToken)); | ||
58 | + } catch (Exception e) { | ||
59 | + throw handleException(e); | ||
60 | + } | ||
61 | + } | ||
62 | + | ||
63 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") | ||
64 | + @RequestMapping(value = "/plugin", method = RequestMethod.POST) | ||
65 | + @ResponseBody | ||
66 | + public PluginMetaData savePlugin(@RequestBody PluginMetaData source) throws ThingsboardException { | ||
67 | + try { | ||
68 | + boolean created = source.getId() == null; | ||
69 | + source.setTenantId(getCurrentUser().getTenantId()); | ||
70 | + PluginMetaData plugin = checkNotNull(pluginService.savePlugin(source)); | ||
71 | + actorService.onPluginStateChange(plugin.getTenantId(), plugin.getId(), | ||
72 | + created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); | ||
73 | + return plugin; | ||
74 | + } catch (Exception e) { | ||
75 | + throw handleException(e); | ||
76 | + } | ||
77 | + } | ||
78 | + | ||
79 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") | ||
80 | + @RequestMapping(value = "/plugin/{pluginId}/activate", method = RequestMethod.POST) | ||
81 | + @ResponseStatus(value = HttpStatus.OK) | ||
82 | + public void activatePluginById(@PathVariable("pluginId") String strPluginId) throws ThingsboardException { | ||
83 | + checkParameter("pluginId", strPluginId); | ||
84 | + try { | ||
85 | + PluginId pluginId = new PluginId(toUUID(strPluginId)); | ||
86 | + PluginMetaData plugin = checkPlugin(pluginService.findPluginById(pluginId)); | ||
87 | + pluginService.activatePluginById(pluginId); | ||
88 | + actorService.onPluginStateChange(plugin.getTenantId(), plugin.getId(), ComponentLifecycleEvent.ACTIVATED); | ||
89 | + } catch (Exception e) { | ||
90 | + throw handleException(e); | ||
91 | + } | ||
92 | + } | ||
93 | + | ||
94 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") | ||
95 | + @RequestMapping(value = "/plugin/{pluginId}/suspend", method = RequestMethod.POST) | ||
96 | + @ResponseStatus(value = HttpStatus.OK) | ||
97 | + public void suspendPluginById(@PathVariable("pluginId") String strPluginId) throws ThingsboardException { | ||
98 | + checkParameter("pluginId", strPluginId); | ||
99 | + try { | ||
100 | + PluginId pluginId = new PluginId(toUUID(strPluginId)); | ||
101 | + PluginMetaData plugin = checkPlugin(pluginService.findPluginById(pluginId)); | ||
102 | + pluginService.suspendPluginById(pluginId); | ||
103 | + actorService.onPluginStateChange(plugin.getTenantId(), plugin.getId(), ComponentLifecycleEvent.SUSPENDED); | ||
104 | + } catch (Exception e) { | ||
105 | + throw handleException(e); | ||
106 | + } | ||
107 | + } | ||
108 | + | ||
109 | + @PreAuthorize("hasAuthority('SYS_ADMIN')") | ||
110 | + @RequestMapping(value = "/plugin/system", params = {"limit"}, method = RequestMethod.GET) | ||
111 | + @ResponseBody | ||
112 | + public TextPageData<PluginMetaData> getSystemPlugins( | ||
113 | + @RequestParam int limit, | ||
114 | + @RequestParam(required = false) String textSearch, | ||
115 | + @RequestParam(required = false) String idOffset, | ||
116 | + @RequestParam(required = false) String textOffset) throws ThingsboardException { | ||
117 | + try { | ||
118 | + TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); | ||
119 | + return checkNotNull(pluginService.findSystemPlugins(pageLink)); | ||
120 | + } catch (Exception e) { | ||
121 | + throw handleException(e); | ||
122 | + } | ||
123 | + } | ||
124 | + | ||
125 | + @PreAuthorize("hasAuthority('SYS_ADMIN')") | ||
126 | + @RequestMapping(value = "/plugin/tenant/{tenantId}", params = {"limit"}, method = RequestMethod.GET) | ||
127 | + @ResponseBody | ||
128 | + public TextPageData<PluginMetaData> getTenantPlugins( | ||
129 | + @PathVariable("tenantId") String strTenantId, | ||
130 | + @RequestParam int limit, | ||
131 | + @RequestParam(required = false) String textSearch, | ||
132 | + @RequestParam(required = false) String idOffset, | ||
133 | + @RequestParam(required = false) String textOffset) throws ThingsboardException { | ||
134 | + checkParameter("tenantId", strTenantId); | ||
135 | + try { | ||
136 | + TenantId tenantId = new TenantId(toUUID(strTenantId)); | ||
137 | + TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); | ||
138 | + return checkNotNull(pluginService.findTenantPlugins(tenantId, pageLink)); | ||
139 | + } catch (Exception e) { | ||
140 | + throw handleException(e); | ||
141 | + } | ||
142 | + } | ||
143 | + | ||
144 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") | ||
145 | + @RequestMapping(value = "/plugins", method = RequestMethod.GET) | ||
146 | + @ResponseBody | ||
147 | + public List<PluginMetaData> getPlugins() throws ThingsboardException { | ||
148 | + try { | ||
149 | + if (getCurrentUser().getAuthority() == Authority.SYS_ADMIN) { | ||
150 | + return checkNotNull(pluginService.findSystemPlugins()); | ||
151 | + } else { | ||
152 | + TenantId tenantId = getCurrentUser().getTenantId(); | ||
153 | + List<PluginMetaData> plugins = checkNotNull(pluginService.findAllTenantPluginsByTenantId(tenantId)); | ||
154 | + plugins.stream() | ||
155 | + .filter(plugin -> plugin.getTenantId().getId().equals(ModelConstants.NULL_UUID)) | ||
156 | + .forEach(plugin -> plugin.setConfiguration(null)); | ||
157 | + return plugins; | ||
158 | + } | ||
159 | + } catch (Exception e) { | ||
160 | + throw handleException(e); | ||
161 | + } | ||
162 | + } | ||
163 | + | ||
164 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | ||
165 | + @RequestMapping(value = "/plugin", params = {"limit"}, method = RequestMethod.GET) | ||
166 | + @ResponseBody | ||
167 | + public TextPageData<PluginMetaData> getTenantPlugins( | ||
168 | + @RequestParam int limit, | ||
169 | + @RequestParam(required = false) String textSearch, | ||
170 | + @RequestParam(required = false) String idOffset, | ||
171 | + @RequestParam(required = false) String textOffset) throws ThingsboardException { | ||
172 | + try { | ||
173 | + TenantId tenantId = getCurrentUser().getTenantId(); | ||
174 | + TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); | ||
175 | + return checkNotNull(pluginService.findTenantPlugins(tenantId, pageLink)); | ||
176 | + } catch (Exception e) { | ||
177 | + throw handleException(e); | ||
178 | + } | ||
179 | + } | ||
180 | + | ||
181 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") | ||
182 | + @RequestMapping(value = "/plugin/{pluginId}", method = RequestMethod.DELETE) | ||
183 | + @ResponseStatus(value = HttpStatus.OK) | ||
184 | + public void deletePlugin(@PathVariable("pluginId") String strPluginId) throws ThingsboardException { | ||
185 | + checkParameter("pluginId", strPluginId); | ||
186 | + try { | ||
187 | + PluginId pluginId = new PluginId(toUUID(strPluginId)); | ||
188 | + PluginMetaData plugin = checkPlugin(pluginService.findPluginById(pluginId)); | ||
189 | + pluginService.deletePluginById(pluginId); | ||
190 | + actorService.onPluginStateChange(plugin.getTenantId(), plugin.getId(), ComponentLifecycleEvent.DELETED); | ||
191 | + } catch (Exception e) { | ||
192 | + throw handleException(e); | ||
193 | + } | ||
194 | + } | ||
195 | + | ||
196 | + | ||
197 | +} |
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.controller; | ||
17 | + | ||
18 | +import org.springframework.http.HttpStatus; | ||
19 | +import org.springframework.security.access.prepost.PreAuthorize; | ||
20 | +import org.springframework.web.bind.annotation.*; | ||
21 | +import org.thingsboard.server.common.data.id.RuleId; | ||
22 | +import org.thingsboard.server.common.data.id.TenantId; | ||
23 | +import org.thingsboard.server.common.data.page.TextPageData; | ||
24 | +import org.thingsboard.server.common.data.page.TextPageLink; | ||
25 | +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; | ||
26 | +import org.thingsboard.server.common.data.plugin.PluginMetaData; | ||
27 | +import org.thingsboard.server.common.data.rule.RuleMetaData; | ||
28 | +import org.thingsboard.server.common.data.security.Authority; | ||
29 | +import org.thingsboard.server.exception.ThingsboardException; | ||
30 | + | ||
31 | +import java.util.List; | ||
32 | + | ||
33 | +@RestController | ||
34 | +@RequestMapping("/api") | ||
35 | +public class RuleController extends BaseController { | ||
36 | + | ||
37 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") | ||
38 | + @RequestMapping(value = "/rule/{ruleId}", method = RequestMethod.GET) | ||
39 | + @ResponseBody | ||
40 | + public RuleMetaData getRuleById(@PathVariable("ruleId") String strRuleId) throws ThingsboardException { | ||
41 | + checkParameter("ruleId", strRuleId); | ||
42 | + try { | ||
43 | + RuleId ruleId = new RuleId(toUUID(strRuleId)); | ||
44 | + return checkRule(ruleService.findRuleById(ruleId)); | ||
45 | + } catch (Exception e) { | ||
46 | + throw handleException(e); | ||
47 | + } | ||
48 | + } | ||
49 | + | ||
50 | + | ||
51 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") | ||
52 | + @RequestMapping(value = "/rule/token/{pluginToken}", method = RequestMethod.GET) | ||
53 | + @ResponseBody | ||
54 | + public List<RuleMetaData> getRulesByPluginToken(@PathVariable("pluginToken") String pluginToken) throws ThingsboardException { | ||
55 | + checkParameter("pluginToken", pluginToken); | ||
56 | + try { | ||
57 | + PluginMetaData plugin = checkPlugin(pluginService.findPluginByApiToken(pluginToken)); | ||
58 | + return ruleService.findPluginRules(plugin.getApiToken()); | ||
59 | + } catch (Exception e) { | ||
60 | + throw handleException(e); | ||
61 | + } | ||
62 | + } | ||
63 | + | ||
64 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") | ||
65 | + @RequestMapping(value = "/rule", method = RequestMethod.POST) | ||
66 | + @ResponseBody | ||
67 | + public RuleMetaData saveRule(@RequestBody RuleMetaData source) throws ThingsboardException { | ||
68 | + try { | ||
69 | + boolean created = source.getId() == null; | ||
70 | + source.setTenantId(getCurrentUser().getTenantId()); | ||
71 | + RuleMetaData rule = checkNotNull(ruleService.saveRule(source)); | ||
72 | + actorService.onRuleStateChange(rule.getTenantId(), rule.getId(), | ||
73 | + created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); | ||
74 | + return rule; | ||
75 | + } catch (Exception e) { | ||
76 | + throw handleException(e); | ||
77 | + } | ||
78 | + } | ||
79 | + | ||
80 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") | ||
81 | + @RequestMapping(value = "/rule/{ruleId}/activate", method = RequestMethod.POST) | ||
82 | + @ResponseStatus(value = HttpStatus.OK) | ||
83 | + public void activateRuleById(@PathVariable("ruleId") String strRuleId) throws ThingsboardException { | ||
84 | + checkParameter("ruleId", strRuleId); | ||
85 | + try { | ||
86 | + RuleId ruleId = new RuleId(toUUID(strRuleId)); | ||
87 | + RuleMetaData rule = checkRule(ruleService.findRuleById(ruleId)); | ||
88 | + ruleService.activateRuleById(ruleId); | ||
89 | + actorService.onRuleStateChange(rule.getTenantId(), rule.getId(), ComponentLifecycleEvent.ACTIVATED); | ||
90 | + } catch (Exception e) { | ||
91 | + throw handleException(e); | ||
92 | + } | ||
93 | + } | ||
94 | + | ||
95 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") | ||
96 | + @RequestMapping(value = "/rule/{ruleId}/suspend", method = RequestMethod.POST) | ||
97 | + @ResponseStatus(value = HttpStatus.OK) | ||
98 | + public void suspendRuleById(@PathVariable("ruleId") String strRuleId) throws ThingsboardException { | ||
99 | + checkParameter("ruleId", strRuleId); | ||
100 | + try { | ||
101 | + RuleId ruleId = new RuleId(toUUID(strRuleId)); | ||
102 | + RuleMetaData rule = checkRule(ruleService.findRuleById(ruleId)); | ||
103 | + ruleService.suspendRuleById(ruleId); | ||
104 | + actorService.onRuleStateChange(rule.getTenantId(), rule.getId(), ComponentLifecycleEvent.SUSPENDED); | ||
105 | + } catch (Exception e) { | ||
106 | + throw handleException(e); | ||
107 | + } | ||
108 | + } | ||
109 | + | ||
110 | + @PreAuthorize("hasAuthority('SYS_ADMIN')") | ||
111 | + @RequestMapping(value = "/rule/system", params = {"limit"}, method = RequestMethod.GET) | ||
112 | + @ResponseBody | ||
113 | + public TextPageData<RuleMetaData> getSystemRules( | ||
114 | + @RequestParam int limit, | ||
115 | + @RequestParam(required = false) String textSearch, | ||
116 | + @RequestParam(required = false) String idOffset, | ||
117 | + @RequestParam(required = false) String textOffset) throws ThingsboardException { | ||
118 | + try { | ||
119 | + TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); | ||
120 | + return checkNotNull(ruleService.findSystemRules(pageLink)); | ||
121 | + } catch (Exception e) { | ||
122 | + throw handleException(e); | ||
123 | + } | ||
124 | + } | ||
125 | + | ||
126 | + @PreAuthorize("hasAuthority('SYS_ADMIN')") | ||
127 | + @RequestMapping(value = "/rule/tenant/{tenantId}", params = {"limit"}, method = RequestMethod.GET) | ||
128 | + @ResponseBody | ||
129 | + public TextPageData<RuleMetaData> getTenantRules( | ||
130 | + @PathVariable("tenantId") String strTenantId, | ||
131 | + @RequestParam int limit, | ||
132 | + @RequestParam(required = false) String textSearch, | ||
133 | + @RequestParam(required = false) String idOffset, | ||
134 | + @RequestParam(required = false) String textOffset) throws ThingsboardException { | ||
135 | + checkParameter("tenantId", strTenantId); | ||
136 | + try { | ||
137 | + TenantId tenantId = new TenantId(toUUID(strTenantId)); | ||
138 | + TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); | ||
139 | + return checkNotNull(ruleService.findTenantRules(tenantId, pageLink)); | ||
140 | + } catch (Exception e) { | ||
141 | + throw handleException(e); | ||
142 | + } | ||
143 | + } | ||
144 | + | ||
145 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") | ||
146 | + @RequestMapping(value = "/rules", method = RequestMethod.GET) | ||
147 | + @ResponseBody | ||
148 | + public List<RuleMetaData> getRules() throws ThingsboardException { | ||
149 | + try { | ||
150 | + if (getCurrentUser().getAuthority() == Authority.SYS_ADMIN) { | ||
151 | + return checkNotNull(ruleService.findSystemRules()); | ||
152 | + } else { | ||
153 | + TenantId tenantId = getCurrentUser().getTenantId(); | ||
154 | + return checkNotNull(ruleService.findAllTenantRulesByTenantId(tenantId)); | ||
155 | + } | ||
156 | + } catch (Exception e) { | ||
157 | + throw handleException(e); | ||
158 | + } | ||
159 | + } | ||
160 | + | ||
161 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | ||
162 | + @RequestMapping(value = "/rule", params = {"limit"}, method = RequestMethod.GET) | ||
163 | + @ResponseBody | ||
164 | + public TextPageData<RuleMetaData> getTenantRules( | ||
165 | + @RequestParam int limit, | ||
166 | + @RequestParam(required = false) String textSearch, | ||
167 | + @RequestParam(required = false) String idOffset, | ||
168 | + @RequestParam(required = false) String textOffset) throws ThingsboardException { | ||
169 | + try { | ||
170 | + TenantId tenantId = getCurrentUser().getTenantId(); | ||
171 | + TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); | ||
172 | + return checkNotNull(ruleService.findTenantRules(tenantId, pageLink)); | ||
173 | + } catch (Exception e) { | ||
174 | + throw handleException(e); | ||
175 | + } | ||
176 | + } | ||
177 | + | ||
178 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") | ||
179 | + @RequestMapping(value = "/rule/{ruleId}", method = RequestMethod.DELETE) | ||
180 | + @ResponseStatus(value = HttpStatus.OK) | ||
181 | + public void deleteRule(@PathVariable("ruleId") String strRuleId) throws ThingsboardException { | ||
182 | + checkParameter("ruleId", strRuleId); | ||
183 | + try { | ||
184 | + RuleId ruleId = new RuleId(toUUID(strRuleId)); | ||
185 | + RuleMetaData rule = checkRule(ruleService.findRuleById(ruleId)); | ||
186 | + ruleService.deleteRuleById(ruleId); | ||
187 | + actorService.onRuleStateChange(rule.getTenantId(), rule.getId(), ComponentLifecycleEvent.DELETED); | ||
188 | + } catch (Exception e) { | ||
189 | + throw handleException(e); | ||
190 | + } | ||
191 | + } | ||
192 | + | ||
193 | +} |
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.controller; | ||
17 | + | ||
18 | +import org.springframework.beans.factory.annotation.Autowired; | ||
19 | +import org.springframework.http.HttpStatus; | ||
20 | +import org.springframework.security.access.prepost.PreAuthorize; | ||
21 | +import org.springframework.web.bind.annotation.*; | ||
22 | +import org.thingsboard.server.common.data.Tenant; | ||
23 | +import org.thingsboard.server.common.data.id.TenantId; | ||
24 | +import org.thingsboard.server.common.data.page.TextPageData; | ||
25 | +import org.thingsboard.server.common.data.page.TextPageLink; | ||
26 | +import org.thingsboard.server.dao.tenant.TenantService; | ||
27 | +import org.thingsboard.server.exception.ThingsboardException; | ||
28 | + | ||
29 | +@RestController | ||
30 | +@RequestMapping("/api") | ||
31 | +public class TenantController extends BaseController { | ||
32 | + | ||
33 | + @Autowired | ||
34 | + private TenantService tenantService; | ||
35 | + | ||
36 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") | ||
37 | + @RequestMapping(value = "/tenant/{tenantId}", method = RequestMethod.GET) | ||
38 | + @ResponseBody | ||
39 | + public Tenant getTenantById(@PathVariable("tenantId") String strTenantId) throws ThingsboardException { | ||
40 | + checkParameter("tenantId", strTenantId); | ||
41 | + try { | ||
42 | + TenantId tenantId = new TenantId(toUUID(strTenantId)); | ||
43 | + checkTenantId(tenantId); | ||
44 | + return checkNotNull(tenantService.findTenantById(tenantId)); | ||
45 | + } catch (Exception e) { | ||
46 | + throw handleException(e); | ||
47 | + } | ||
48 | + } | ||
49 | + | ||
50 | + @PreAuthorize("hasAuthority('SYS_ADMIN')") | ||
51 | + @RequestMapping(value = "/tenant", method = RequestMethod.POST) | ||
52 | + @ResponseBody | ||
53 | + public Tenant saveTenant(@RequestBody Tenant tenant) throws ThingsboardException { | ||
54 | + try { | ||
55 | + return checkNotNull(tenantService.saveTenant(tenant)); | ||
56 | + } catch (Exception e) { | ||
57 | + throw handleException(e); | ||
58 | + } | ||
59 | + } | ||
60 | + | ||
61 | + @PreAuthorize("hasAuthority('SYS_ADMIN')") | ||
62 | + @RequestMapping(value = "/tenant/{tenantId}", method = RequestMethod.DELETE) | ||
63 | + @ResponseStatus(value = HttpStatus.OK) | ||
64 | + public void deleteTenant(@PathVariable("tenantId") String strTenantId) throws ThingsboardException { | ||
65 | + checkParameter("tenantId", strTenantId); | ||
66 | + try { | ||
67 | + TenantId tenantId = new TenantId(toUUID(strTenantId)); | ||
68 | + tenantService.deleteTenant(tenantId); | ||
69 | + } catch (Exception e) { | ||
70 | + throw handleException(e); | ||
71 | + } | ||
72 | + } | ||
73 | + | ||
74 | + @PreAuthorize("hasAuthority('SYS_ADMIN')") | ||
75 | + @RequestMapping(value = "/tenants", params = { "limit" }, method = RequestMethod.GET) | ||
76 | + @ResponseBody | ||
77 | + public TextPageData<Tenant> getTenants(@RequestParam int limit, | ||
78 | + @RequestParam(required = false) String textSearch, | ||
79 | + @RequestParam(required = false) String idOffset, | ||
80 | + @RequestParam(required = false) String textOffset) throws ThingsboardException { | ||
81 | + try { | ||
82 | + TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); | ||
83 | + return checkNotNull(tenantService.findTenants(pageLink)); | ||
84 | + } catch (Exception e) { | ||
85 | + throw handleException(e); | ||
86 | + } | ||
87 | + } | ||
88 | + | ||
89 | +} |
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.controller; | ||
17 | + | ||
18 | +import org.springframework.beans.factory.annotation.Autowired; | ||
19 | +import org.springframework.http.HttpStatus; | ||
20 | +import org.springframework.security.access.prepost.PreAuthorize; | ||
21 | +import org.springframework.web.bind.annotation.*; | ||
22 | +import org.thingsboard.server.common.data.User; | ||
23 | +import org.thingsboard.server.common.data.id.CustomerId; | ||
24 | +import org.thingsboard.server.common.data.id.TenantId; | ||
25 | +import org.thingsboard.server.common.data.id.UserId; | ||
26 | +import org.thingsboard.server.common.data.page.TextPageData; | ||
27 | +import org.thingsboard.server.common.data.page.TextPageLink; | ||
28 | +import org.thingsboard.server.common.data.security.Authority; | ||
29 | +import org.thingsboard.server.common.data.security.UserCredentials; | ||
30 | +import org.thingsboard.server.exception.ThingsboardErrorCode; | ||
31 | +import org.thingsboard.server.exception.ThingsboardException; | ||
32 | +import org.thingsboard.server.service.mail.MailService; | ||
33 | +import org.thingsboard.server.service.security.model.SecurityUser; | ||
34 | + | ||
35 | +import javax.servlet.http.HttpServletRequest; | ||
36 | + | ||
37 | +@RestController | ||
38 | +@RequestMapping("/api") | ||
39 | +public class UserController extends BaseController { | ||
40 | + | ||
41 | + @Autowired | ||
42 | + private MailService mailService; | ||
43 | + | ||
44 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") | ||
45 | + @RequestMapping(value = "/user/{userId}", method = RequestMethod.GET) | ||
46 | + @ResponseBody | ||
47 | + public User getUserById(@PathVariable("userId") String strUserId) throws ThingsboardException { | ||
48 | + checkParameter("userId", strUserId); | ||
49 | + try { | ||
50 | + UserId userId = new UserId(toUUID(strUserId)); | ||
51 | + SecurityUser authUser = getCurrentUser(); | ||
52 | + if (authUser.getAuthority() == Authority.CUSTOMER_USER && !authUser.getId().equals(userId)) { | ||
53 | + throw new ThingsboardException("You don't have permission to perform this operation!", | ||
54 | + ThingsboardErrorCode.PERMISSION_DENIED); | ||
55 | + } | ||
56 | + return checkUserId(userId); | ||
57 | + } catch (Exception e) { | ||
58 | + throw handleException(e); | ||
59 | + } | ||
60 | + } | ||
61 | + | ||
62 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") | ||
63 | + @RequestMapping(value = "/user", method = RequestMethod.POST) | ||
64 | + @ResponseBody | ||
65 | + public User saveUser(@RequestBody User user, | ||
66 | + HttpServletRequest request) throws ThingsboardException { | ||
67 | + try { | ||
68 | + SecurityUser authUser = getCurrentUser(); | ||
69 | + if (authUser.getAuthority() == Authority.CUSTOMER_USER && !authUser.getId().equals(user.getId())) { | ||
70 | + throw new ThingsboardException("You don't have permission to perform this operation!", | ||
71 | + ThingsboardErrorCode.PERMISSION_DENIED); | ||
72 | + } | ||
73 | + boolean sendEmail = user.getId() == null; | ||
74 | + if (getCurrentUser().getAuthority() == Authority.TENANT_ADMIN) { | ||
75 | + user.setTenantId(getCurrentUser().getTenantId()); | ||
76 | + } | ||
77 | + User savedUser = checkNotNull(userService.saveUser(user)); | ||
78 | + if (sendEmail) { | ||
79 | + UserCredentials userCredentials = userService.findUserCredentialsByUserId(savedUser.getId()); | ||
80 | + String baseUrl = String.format("%s://%s:%d", | ||
81 | + request.getScheme(), | ||
82 | + request.getServerName(), | ||
83 | + request.getServerPort()); | ||
84 | + String activateUrl = String.format("%s/api/noauth/activate?activateToken=%s", baseUrl, | ||
85 | + userCredentials.getActivateToken()); | ||
86 | + String email = savedUser.getEmail(); | ||
87 | + try { | ||
88 | + mailService.sendActivationEmail(activateUrl, email); | ||
89 | + } catch (ThingsboardException e) { | ||
90 | + userService.deleteUser(savedUser.getId()); | ||
91 | + throw e; | ||
92 | + } | ||
93 | + } | ||
94 | + return savedUser; | ||
95 | + } catch (Exception e) { | ||
96 | + throw handleException(e); | ||
97 | + } | ||
98 | + } | ||
99 | + | ||
100 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") | ||
101 | + @RequestMapping(value = "/user/sendActivationMail", method = RequestMethod.POST) | ||
102 | + @ResponseStatus(value = HttpStatus.OK) | ||
103 | + public void sendActivationEmail( | ||
104 | + @RequestParam(value = "email") String email, | ||
105 | + HttpServletRequest request) throws ThingsboardException { | ||
106 | + try { | ||
107 | + User user = checkNotNull(userService.findUserByEmail(email)); | ||
108 | + UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getId()); | ||
109 | + if (!userCredentials.isEnabled()) { | ||
110 | + String baseUrl = String.format("%s://%s:%d", | ||
111 | + request.getScheme(), | ||
112 | + request.getServerName(), | ||
113 | + request.getServerPort()); | ||
114 | + String activateUrl = String.format("%s/api/noauth/activate?activateToken=%s", baseUrl, | ||
115 | + userCredentials.getActivateToken()); | ||
116 | + mailService.sendActivationEmail(activateUrl, email); | ||
117 | + } else { | ||
118 | + throw new ThingsboardException("User is already active!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); | ||
119 | + } | ||
120 | + } catch (Exception e) { | ||
121 | + throw handleException(e); | ||
122 | + } | ||
123 | + } | ||
124 | + | ||
125 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") | ||
126 | + @RequestMapping(value = "/user/{userId}", method = RequestMethod.DELETE) | ||
127 | + @ResponseStatus(value = HttpStatus.OK) | ||
128 | + public void deleteUser(@PathVariable("userId") String strUserId) throws ThingsboardException { | ||
129 | + checkParameter("userId", strUserId); | ||
130 | + try { | ||
131 | + UserId userId = new UserId(toUUID(strUserId)); | ||
132 | + checkUserId(userId); | ||
133 | + userService.deleteUser(userId); | ||
134 | + } catch (Exception e) { | ||
135 | + throw handleException(e); | ||
136 | + } | ||
137 | + } | ||
138 | + | ||
139 | + @PreAuthorize("hasAuthority('SYS_ADMIN')") | ||
140 | + @RequestMapping(value = "/tenant/{tenantId}/users", params = { "limit" }, method = RequestMethod.GET) | ||
141 | + @ResponseBody | ||
142 | + public TextPageData<User> getTenantAdmins( | ||
143 | + @PathVariable("tenantId") String strTenantId, | ||
144 | + @RequestParam int limit, | ||
145 | + @RequestParam(required = false) String textSearch, | ||
146 | + @RequestParam(required = false) String idOffset, | ||
147 | + @RequestParam(required = false) String textOffset) throws ThingsboardException { | ||
148 | + checkParameter("tenantId", strTenantId); | ||
149 | + try { | ||
150 | + TenantId tenantId = new TenantId(toUUID(strTenantId)); | ||
151 | + TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); | ||
152 | + return checkNotNull(userService.findTenantAdmins(tenantId, pageLink)); | ||
153 | + } catch (Exception e) { | ||
154 | + throw handleException(e); | ||
155 | + } | ||
156 | + } | ||
157 | + | ||
158 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | ||
159 | + @RequestMapping(value = "/customer/{customerId}/users", params = { "limit" }, method = RequestMethod.GET) | ||
160 | + @ResponseBody | ||
161 | + public TextPageData<User> getCustomerUsers( | ||
162 | + @PathVariable("customerId") String strCustomerId, | ||
163 | + @RequestParam int limit, | ||
164 | + @RequestParam(required = false) String textSearch, | ||
165 | + @RequestParam(required = false) String idOffset, | ||
166 | + @RequestParam(required = false) String textOffset) throws ThingsboardException { | ||
167 | + checkParameter("customerId", strCustomerId); | ||
168 | + try { | ||
169 | + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); | ||
170 | + checkCustomerId(customerId); | ||
171 | + TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); | ||
172 | + TenantId tenantId = getCurrentUser().getTenantId(); | ||
173 | + return checkNotNull(userService.findCustomerUsers(tenantId, customerId, pageLink)); | ||
174 | + } catch (Exception e) { | ||
175 | + throw handleException(e); | ||
176 | + } | ||
177 | + } | ||
178 | + | ||
179 | +} |
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.controller; | ||
17 | + | ||
18 | +import org.springframework.http.HttpStatus; | ||
19 | +import org.springframework.security.access.prepost.PreAuthorize; | ||
20 | +import org.springframework.web.bind.annotation.*; | ||
21 | +import org.thingsboard.server.common.data.id.TenantId; | ||
22 | +import org.thingsboard.server.common.data.id.WidgetTypeId; | ||
23 | +import org.thingsboard.server.common.data.security.Authority; | ||
24 | +import org.thingsboard.server.common.data.widget.WidgetType; | ||
25 | +import org.thingsboard.server.dao.model.ModelConstants; | ||
26 | +import org.thingsboard.server.exception.ThingsboardException; | ||
27 | + | ||
28 | +import java.util.List; | ||
29 | + | ||
30 | +@RestController | ||
31 | +@RequestMapping("/api") | ||
32 | +public class WidgetTypeController extends BaseController { | ||
33 | + | ||
34 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") | ||
35 | + @RequestMapping(value = "/widgetType/{widgetTypeId}", method = RequestMethod.GET) | ||
36 | + @ResponseBody | ||
37 | + public WidgetType getWidgetTypeById(@PathVariable("widgetTypeId") String strWidgetTypeId) throws ThingsboardException { | ||
38 | + checkParameter("widgetTypeId", strWidgetTypeId); | ||
39 | + try { | ||
40 | + WidgetTypeId widgetTypeId = new WidgetTypeId(toUUID(strWidgetTypeId)); | ||
41 | + return checkWidgetTypeId(widgetTypeId, false); | ||
42 | + } catch (Exception e) { | ||
43 | + throw handleException(e); | ||
44 | + } | ||
45 | + } | ||
46 | + | ||
47 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") | ||
48 | + @RequestMapping(value = "/widgetType", method = RequestMethod.POST) | ||
49 | + @ResponseBody | ||
50 | + public WidgetType saveWidgetType(@RequestBody WidgetType widgetType) throws ThingsboardException { | ||
51 | + try { | ||
52 | + if (getCurrentUser().getAuthority() == Authority.SYS_ADMIN) { | ||
53 | + widgetType.setTenantId(new TenantId(ModelConstants.NULL_UUID)); | ||
54 | + } else { | ||
55 | + widgetType.setTenantId(getCurrentUser().getTenantId()); | ||
56 | + } | ||
57 | + return checkNotNull(widgetTypeService.saveWidgetType(widgetType)); | ||
58 | + } catch (Exception e) { | ||
59 | + throw handleException(e); | ||
60 | + } | ||
61 | + } | ||
62 | + | ||
63 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") | ||
64 | + @RequestMapping(value = "/widgetType/{widgetTypeId}", method = RequestMethod.DELETE) | ||
65 | + @ResponseStatus(value = HttpStatus.OK) | ||
66 | + public void deleteWidgetType(@PathVariable("widgetTypeId") String strWidgetTypeId) throws ThingsboardException { | ||
67 | + checkParameter("widgetTypeId", strWidgetTypeId); | ||
68 | + try { | ||
69 | + WidgetTypeId widgetTypeId = new WidgetTypeId(toUUID(strWidgetTypeId)); | ||
70 | + checkWidgetTypeId(widgetTypeId, true); | ||
71 | + widgetTypeService.deleteWidgetType(widgetTypeId); | ||
72 | + } catch (Exception e) { | ||
73 | + throw handleException(e); | ||
74 | + } | ||
75 | + } | ||
76 | + | ||
77 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") | ||
78 | + @RequestMapping(value = "/widgetTypes", params = { "isSystem", "bundleAlias"}, method = RequestMethod.GET) | ||
79 | + @ResponseBody | ||
80 | + public List<WidgetType> getBundleWidgetTypes( | ||
81 | + @RequestParam boolean isSystem, | ||
82 | + @RequestParam String bundleAlias) throws ThingsboardException { | ||
83 | + try { | ||
84 | + TenantId tenantId; | ||
85 | + if (isSystem) { | ||
86 | + tenantId = new TenantId(ModelConstants.NULL_UUID); | ||
87 | + } else { | ||
88 | + tenantId = getCurrentUser().getTenantId(); | ||
89 | + } | ||
90 | + return checkNotNull(widgetTypeService.findWidgetTypesByTenantIdAndBundleAlias(tenantId, bundleAlias)); | ||
91 | + } catch (Exception e) { | ||
92 | + throw handleException(e); | ||
93 | + } | ||
94 | + } | ||
95 | + | ||
96 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") | ||
97 | + @RequestMapping(value = "/widgetType", params = { "isSystem", "bundleAlias", "alias" }, method = RequestMethod.GET) | ||
98 | + @ResponseBody | ||
99 | + public WidgetType getWidgetType( | ||
100 | + @RequestParam boolean isSystem, | ||
101 | + @RequestParam String bundleAlias, | ||
102 | + @RequestParam String alias) throws ThingsboardException { | ||
103 | + try { | ||
104 | + TenantId tenantId; | ||
105 | + if (isSystem) { | ||
106 | + tenantId = new TenantId(ModelConstants.NULL_UUID); | ||
107 | + } else { | ||
108 | + tenantId = getCurrentUser().getTenantId(); | ||
109 | + } | ||
110 | + WidgetType widgetType = widgetTypeService.findWidgetTypeByTenantIdBundleAliasAndAlias(tenantId, bundleAlias, alias); | ||
111 | + checkWidgetType(widgetType, false); | ||
112 | + return widgetType; | ||
113 | + } catch (Exception e) { | ||
114 | + throw handleException(e); | ||
115 | + } | ||
116 | + } | ||
117 | + | ||
118 | +} |
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.controller; | ||
17 | + | ||
18 | +import org.springframework.http.HttpStatus; | ||
19 | +import org.springframework.security.access.prepost.PreAuthorize; | ||
20 | +import org.springframework.web.bind.annotation.*; | ||
21 | +import org.thingsboard.server.common.data.id.TenantId; | ||
22 | +import org.thingsboard.server.common.data.id.WidgetsBundleId; | ||
23 | +import org.thingsboard.server.common.data.page.TextPageData; | ||
24 | +import org.thingsboard.server.common.data.page.TextPageLink; | ||
25 | +import org.thingsboard.server.common.data.security.Authority; | ||
26 | +import org.thingsboard.server.common.data.widget.WidgetsBundle; | ||
27 | +import org.thingsboard.server.dao.model.ModelConstants; | ||
28 | +import org.thingsboard.server.exception.ThingsboardException; | ||
29 | + | ||
30 | +import java.util.List; | ||
31 | + | ||
32 | +@RestController | ||
33 | +@RequestMapping("/api") | ||
34 | +public class WidgetsBundleController extends BaseController { | ||
35 | + | ||
36 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") | ||
37 | + @RequestMapping(value = "/widgetsBundle/{widgetsBundleId}", method = RequestMethod.GET) | ||
38 | + @ResponseBody | ||
39 | + public WidgetsBundle getWidgetsBundleById(@PathVariable("widgetsBundleId") String strWidgetsBundleId) throws ThingsboardException { | ||
40 | + checkParameter("widgetsBundleId", strWidgetsBundleId); | ||
41 | + try { | ||
42 | + WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId)); | ||
43 | + return checkWidgetsBundleId(widgetsBundleId, false); | ||
44 | + } catch (Exception e) { | ||
45 | + throw handleException(e); | ||
46 | + } | ||
47 | + } | ||
48 | + | ||
49 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") | ||
50 | + @RequestMapping(value = "/widgetsBundle", method = RequestMethod.POST) | ||
51 | + @ResponseBody | ||
52 | + public WidgetsBundle saveWidgetsBundle(@RequestBody WidgetsBundle widgetsBundle) throws ThingsboardException { | ||
53 | + try { | ||
54 | + if (getCurrentUser().getAuthority() == Authority.SYS_ADMIN) { | ||
55 | + widgetsBundle.setTenantId(new TenantId(ModelConstants.NULL_UUID)); | ||
56 | + } else { | ||
57 | + widgetsBundle.setTenantId(getCurrentUser().getTenantId()); | ||
58 | + } | ||
59 | + return checkNotNull(widgetsBundleService.saveWidgetsBundle(widgetsBundle)); | ||
60 | + } catch (Exception e) { | ||
61 | + throw handleException(e); | ||
62 | + } | ||
63 | + } | ||
64 | + | ||
65 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") | ||
66 | + @RequestMapping(value = "/widgetsBundle/{widgetsBundleId}", method = RequestMethod.DELETE) | ||
67 | + @ResponseStatus(value = HttpStatus.OK) | ||
68 | + public void deleteWidgetsBundle(@PathVariable("widgetsBundleId") String strWidgetsBundleId) throws ThingsboardException { | ||
69 | + checkParameter("widgetsBundleId", strWidgetsBundleId); | ||
70 | + try { | ||
71 | + WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId)); | ||
72 | + checkWidgetsBundleId(widgetsBundleId, true); | ||
73 | + widgetsBundleService.deleteWidgetsBundle(widgetsBundleId); | ||
74 | + } catch (Exception e) { | ||
75 | + throw handleException(e); | ||
76 | + } | ||
77 | + } | ||
78 | + | ||
79 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") | ||
80 | + @RequestMapping(value = "/widgetsBundles", params = { "limit" }, method = RequestMethod.GET) | ||
81 | + @ResponseBody | ||
82 | + public TextPageData<WidgetsBundle> getWidgetsBundles( | ||
83 | + @RequestParam int limit, | ||
84 | + @RequestParam(required = false) String textSearch, | ||
85 | + @RequestParam(required = false) String idOffset, | ||
86 | + @RequestParam(required = false) String textOffset) throws ThingsboardException { | ||
87 | + try { | ||
88 | + TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); | ||
89 | + if (getCurrentUser().getAuthority() == Authority.SYS_ADMIN) { | ||
90 | + return checkNotNull(widgetsBundleService.findSystemWidgetsBundlesByPageLink(pageLink)); | ||
91 | + } else { | ||
92 | + TenantId tenantId = getCurrentUser().getTenantId(); | ||
93 | + return checkNotNull(widgetsBundleService.findAllTenantWidgetsBundlesByTenantIdAndPageLink(tenantId, pageLink)); | ||
94 | + } | ||
95 | + } catch (Exception e) { | ||
96 | + throw handleException(e); | ||
97 | + } | ||
98 | + } | ||
99 | + | ||
100 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") | ||
101 | + @RequestMapping(value = "/widgetsBundles", method = RequestMethod.GET) | ||
102 | + @ResponseBody | ||
103 | + public List<WidgetsBundle> getWidgetsBundles() throws ThingsboardException { | ||
104 | + try { | ||
105 | + if (getCurrentUser().getAuthority() == Authority.SYS_ADMIN) { | ||
106 | + return checkNotNull(widgetsBundleService.findSystemWidgetsBundles()); | ||
107 | + } else { | ||
108 | + TenantId tenantId = getCurrentUser().getTenantId(); | ||
109 | + return checkNotNull(widgetsBundleService.findAllTenantWidgetsBundlesByTenantId(tenantId)); | ||
110 | + } | ||
111 | + } catch (Exception e) { | ||
112 | + throw handleException(e); | ||
113 | + } | ||
114 | + } | ||
115 | + | ||
116 | +} |
application/src/main/java/org/thingsboard/server/controller/plugin/PluginApiController.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.controller.plugin; | ||
17 | + | ||
18 | + | ||
19 | +import lombok.extern.slf4j.Slf4j; | ||
20 | +import org.springframework.beans.factory.annotation.Autowired; | ||
21 | +import org.springframework.http.HttpStatus; | ||
22 | +import org.springframework.http.RequestEntity; | ||
23 | +import org.springframework.http.ResponseEntity; | ||
24 | +import org.springframework.security.access.prepost.PreAuthorize; | ||
25 | +import org.springframework.web.bind.annotation.PathVariable; | ||
26 | +import org.springframework.web.bind.annotation.RequestMapping; | ||
27 | +import org.springframework.web.bind.annotation.ResponseStatus; | ||
28 | +import org.springframework.web.bind.annotation.RestController; | ||
29 | +import org.springframework.web.context.request.async.DeferredResult; | ||
30 | +import org.thingsboard.server.actors.service.ActorService; | ||
31 | +import org.thingsboard.server.common.data.id.CustomerId; | ||
32 | +import org.thingsboard.server.common.data.id.TenantId; | ||
33 | +import org.thingsboard.server.common.data.plugin.PluginMetaData; | ||
34 | +import org.thingsboard.server.controller.BaseController; | ||
35 | +import org.thingsboard.server.dao.model.ModelConstants; | ||
36 | +import org.thingsboard.server.dao.plugin.PluginService; | ||
37 | +import org.thingsboard.server.exception.ThingsboardException; | ||
38 | +import org.thingsboard.server.extensions.api.plugins.PluginApiCallSecurityContext; | ||
39 | +import org.thingsboard.server.extensions.api.plugins.PluginConstants; | ||
40 | +import org.thingsboard.server.extensions.api.plugins.rest.BasicPluginRestMsg; | ||
41 | +import org.thingsboard.server.extensions.api.plugins.rest.RestRequest; | ||
42 | + | ||
43 | +import javax.servlet.http.HttpServletRequest; | ||
44 | + | ||
45 | +@RestController | ||
46 | +@RequestMapping(PluginConstants.PLUGIN_URL_PREFIX) | ||
47 | +@Slf4j | ||
48 | +public class PluginApiController extends BaseController { | ||
49 | + | ||
50 | + @Autowired | ||
51 | + private ActorService actorService; | ||
52 | + | ||
53 | + @Autowired | ||
54 | + private PluginService pluginService; | ||
55 | + | ||
56 | + @SuppressWarnings("rawtypes") | ||
57 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") | ||
58 | + @RequestMapping(value = "/{pluginToken}/**") | ||
59 | + @ResponseStatus(value = HttpStatus.OK) | ||
60 | + public DeferredResult<ResponseEntity> processRequest( | ||
61 | + @PathVariable("pluginToken") String pluginToken, | ||
62 | + RequestEntity<byte[]> requestEntity, | ||
63 | + HttpServletRequest request) | ||
64 | + throws ThingsboardException { | ||
65 | + log.debug("[{}] Going to process requst uri: {}", pluginToken, requestEntity.getUrl()); | ||
66 | + DeferredResult<ResponseEntity> result = new DeferredResult<ResponseEntity>(); | ||
67 | + PluginMetaData pluginMd = pluginService.findPluginByApiToken(pluginToken); | ||
68 | + if (pluginMd == null) { | ||
69 | + result.setErrorResult(new PluginNotFoundException("Plugin with token: " + pluginToken + " not found!")); | ||
70 | + } else { | ||
71 | + TenantId tenantId = getCurrentUser().getTenantId(); | ||
72 | + CustomerId customerId = getCurrentUser().getCustomerId(); | ||
73 | + if (validatePluginAccess(pluginMd, tenantId, customerId)) { | ||
74 | + if(ModelConstants.NULL_UUID.equals(tenantId.getId())){ | ||
75 | + tenantId = null; | ||
76 | + } | ||
77 | + PluginApiCallSecurityContext securityCtx = new PluginApiCallSecurityContext(pluginMd.getTenantId(), pluginMd.getId(), tenantId, customerId); | ||
78 | + actorService.process(new BasicPluginRestMsg(securityCtx, new RestRequest(requestEntity, request), result)); | ||
79 | + } else { | ||
80 | + result.setResult(new ResponseEntity<>(HttpStatus.FORBIDDEN)); | ||
81 | + } | ||
82 | + | ||
83 | + } | ||
84 | + return result; | ||
85 | + } | ||
86 | + | ||
87 | + public static boolean validatePluginAccess(PluginMetaData pluginMd, TenantId tenantId, CustomerId customerId) { | ||
88 | + boolean systemAdministrator = tenantId == null || ModelConstants.NULL_UUID.equals(tenantId.getId()); | ||
89 | + boolean tenantAdministrator = !systemAdministrator && (customerId == null || ModelConstants.NULL_UUID.equals(customerId.getId())); | ||
90 | + boolean systemPlugin = ModelConstants.NULL_UUID.equals(pluginMd.getTenantId().getId()); | ||
91 | + | ||
92 | + boolean validUser = false; | ||
93 | + if (systemPlugin) { | ||
94 | + if (pluginMd.isPublicAccess() || systemAdministrator) { | ||
95 | + // All users can access public system plugins. Only system | ||
96 | + // users can access private system plugins | ||
97 | + validUser = true; | ||
98 | + } | ||
99 | + } else { | ||
100 | + if ((pluginMd.isPublicAccess() || tenantAdministrator) && tenantId.equals(pluginMd.getTenantId())) { | ||
101 | + // All tenant users can access public tenant plugins. Only tenant | ||
102 | + // administrator can access private tenant plugins | ||
103 | + validUser = true; | ||
104 | + } | ||
105 | + } | ||
106 | + return validUser; | ||
107 | + } | ||
108 | +} |
application/src/main/java/org/thingsboard/server/controller/plugin/PluginNotFoundException.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.controller.plugin; | ||
17 | + | ||
18 | +import org.springframework.http.HttpStatus; | ||
19 | +import org.springframework.web.bind.annotation.ResponseStatus; | ||
20 | + | ||
21 | +@ResponseStatus(HttpStatus.NOT_FOUND) | ||
22 | +public class PluginNotFoundException extends RuntimeException { | ||
23 | + | ||
24 | + private static final long serialVersionUID = 1L; | ||
25 | + | ||
26 | + public PluginNotFoundException(String message){ | ||
27 | + super(message); | ||
28 | + } | ||
29 | + | ||
30 | +} |
application/src/main/java/org/thingsboard/server/controller/plugin/PluginWebSocketHandler.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.controller.plugin; | ||
17 | + | ||
18 | +import java.io.IOException; | ||
19 | +import java.net.URI; | ||
20 | +import java.security.InvalidParameterException; | ||
21 | +import java.util.UUID; | ||
22 | +import java.util.concurrent.ConcurrentHashMap; | ||
23 | +import java.util.concurrent.ConcurrentMap; | ||
24 | + | ||
25 | +import lombok.extern.slf4j.Slf4j; | ||
26 | +import org.springframework.context.annotation.Lazy; | ||
27 | +import org.springframework.web.bind.annotation.RequestMapping; | ||
28 | +import org.springframework.web.bind.annotation.RestController; | ||
29 | +import org.thingsboard.server.actors.service.ActorService; | ||
30 | +import org.thingsboard.server.config.WebSocketConfiguration; | ||
31 | +import org.thingsboard.server.extensions.api.plugins.PluginConstants; | ||
32 | +import org.thingsboard.server.service.security.model.SecurityUser; | ||
33 | +import org.thingsboard.server.common.data.id.CustomerId; | ||
34 | +import org.thingsboard.server.common.data.id.TenantId; | ||
35 | +import org.thingsboard.server.common.data.plugin.PluginMetaData; | ||
36 | +import org.thingsboard.server.dao.plugin.PluginService; | ||
37 | +import org.thingsboard.server.extensions.api.plugins.PluginApiCallSecurityContext; | ||
38 | +import org.thingsboard.server.extensions.api.plugins.ws.BasicPluginWebsocketSessionRef; | ||
39 | +import org.thingsboard.server.extensions.api.plugins.ws.PluginWebsocketSessionRef; | ||
40 | +import org.thingsboard.server.extensions.api.plugins.ws.SessionEvent; | ||
41 | +import org.thingsboard.server.extensions.api.plugins.ws.msg.PluginWebsocketMsg; | ||
42 | +import org.thingsboard.server.extensions.api.plugins.ws.msg.SessionEventPluginWebSocketMsg; | ||
43 | +import org.thingsboard.server.extensions.api.plugins.ws.msg.TextPluginWebSocketMsg; | ||
44 | +import org.slf4j.Logger; | ||
45 | +import org.slf4j.LoggerFactory; | ||
46 | +import org.springframework.beans.factory.annotation.Autowired; | ||
47 | +import org.springframework.stereotype.Service; | ||
48 | +import org.springframework.web.socket.CloseStatus; | ||
49 | +import org.springframework.web.socket.TextMessage; | ||
50 | +import org.springframework.web.socket.WebSocketSession; | ||
51 | +import org.springframework.web.socket.handler.TextWebSocketHandler; | ||
52 | + | ||
53 | +@Service | ||
54 | +@Slf4j | ||
55 | +public class PluginWebSocketHandler extends TextWebSocketHandler implements PluginWebSocketMsgEndpoint { | ||
56 | + | ||
57 | + private static final ConcurrentMap<String, SessionMetaData> internalSessionMap = new ConcurrentHashMap<>(); | ||
58 | + private static final ConcurrentMap<String, String> externalSessionMap = new ConcurrentHashMap<>(); | ||
59 | + | ||
60 | + @Autowired @Lazy | ||
61 | + private ActorService actorService; | ||
62 | + | ||
63 | + @Autowired @Lazy | ||
64 | + private PluginService pluginService; | ||
65 | + | ||
66 | + @Override | ||
67 | + public void handleTextMessage(WebSocketSession session, TextMessage message) { | ||
68 | + try { | ||
69 | + log.info("[{}] Processing {}", session.getId(), message); | ||
70 | + SessionMetaData sessionMd = internalSessionMap.get(session.getId()); | ||
71 | + if (sessionMd != null) { | ||
72 | + actorService.process(new TextPluginWebSocketMsg(sessionMd.sessionRef, message.getPayload())); | ||
73 | + } else { | ||
74 | + log.warn("[{}] Failed to find session", session.getId()); | ||
75 | + session.close(CloseStatus.SERVER_ERROR.withReason("Session not found!")); | ||
76 | + } | ||
77 | + session.sendMessage(message); | ||
78 | + } catch (IOException e) { | ||
79 | + log.warn("IO error", e); | ||
80 | + } | ||
81 | + } | ||
82 | + | ||
83 | + @Override | ||
84 | + public void afterConnectionEstablished(WebSocketSession session) throws Exception { | ||
85 | + super.afterConnectionEstablished(session); | ||
86 | + try { | ||
87 | + String internalSessionId = session.getId(); | ||
88 | + PluginWebsocketSessionRef sessionRef = toRef(session); | ||
89 | + String externalSessionId = sessionRef.getSessionId(); | ||
90 | + internalSessionMap.put(internalSessionId, new SessionMetaData(session, sessionRef)); | ||
91 | + externalSessionMap.put(externalSessionId, internalSessionId); | ||
92 | + actorService.process(new SessionEventPluginWebSocketMsg(sessionRef, SessionEvent.onEstablished())); | ||
93 | + log.info("[{}][{}] Session is started", externalSessionId, session.getId()); | ||
94 | + } catch (InvalidParameterException e) { | ||
95 | + log.warn("[[{}] Failed to start session", session.getId(), e); | ||
96 | + session.close(CloseStatus.BAD_DATA.withReason(e.getMessage())); | ||
97 | + } catch (Exception e) { | ||
98 | + log.warn("[{}] Failed to start session", session.getId(), e); | ||
99 | + session.close(CloseStatus.SERVER_ERROR.withReason(e.getMessage())); | ||
100 | + } | ||
101 | + } | ||
102 | + | ||
103 | + @Override | ||
104 | + public void handleTransportError(WebSocketSession session, Throwable tError) throws Exception { | ||
105 | + super.handleTransportError(session, tError); | ||
106 | + SessionMetaData sessionMd = internalSessionMap.get(session.getId()); | ||
107 | + if (sessionMd != null) { | ||
108 | + actorService.process(new SessionEventPluginWebSocketMsg(sessionMd.sessionRef, SessionEvent.onError(tError))); | ||
109 | + } else { | ||
110 | + log.warn("[{}] Failed to find session", session.getId()); | ||
111 | + } | ||
112 | + log.trace("[{}] Session transport error", session.getId(), tError); | ||
113 | + } | ||
114 | + | ||
115 | + @Override | ||
116 | + public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception { | ||
117 | + super.afterConnectionClosed(session, closeStatus); | ||
118 | + SessionMetaData sessionMd = internalSessionMap.remove(session.getId()); | ||
119 | + if (sessionMd != null) { | ||
120 | + externalSessionMap.remove(sessionMd.sessionRef.getSessionId()); | ||
121 | + actorService.process(new SessionEventPluginWebSocketMsg(sessionMd.sessionRef, SessionEvent.onClosed())); | ||
122 | + } | ||
123 | + log.info("[{}] Session is closed", session.getId()); | ||
124 | + } | ||
125 | + | ||
126 | + private PluginWebsocketSessionRef toRef(WebSocketSession session) throws IOException { | ||
127 | + URI sessionUri = session.getUri(); | ||
128 | + String path = sessionUri.getPath(); | ||
129 | + path = path.substring(WebSocketConfiguration.WS_PLUGIN_PREFIX.length()); | ||
130 | + if (path.length() == 0) { | ||
131 | + throw new IllegalArgumentException("URL should contain plugin token!"); | ||
132 | + } | ||
133 | + String[] pathElements = path.split("/"); | ||
134 | + String pluginToken = pathElements[0]; | ||
135 | + // TODO: cache | ||
136 | + PluginMetaData pluginMd = pluginService.findPluginByApiToken(pluginToken); | ||
137 | + if (pluginMd == null) { | ||
138 | + throw new InvalidParameterException("Can't find plugin with specified token!"); | ||
139 | + } else { | ||
140 | + SecurityUser currentUser = (SecurityUser) session.getAttributes().get(WebSocketConfiguration.WS_SECURITY_USER_ATTRIBUTE); | ||
141 | + TenantId tenantId = currentUser.getTenantId(); | ||
142 | + CustomerId customerId = currentUser.getCustomerId(); | ||
143 | + if (PluginApiController.validatePluginAccess(pluginMd, tenantId, customerId)) { | ||
144 | + PluginApiCallSecurityContext securityCtx = new PluginApiCallSecurityContext(pluginMd.getTenantId(), pluginMd.getId(), tenantId, | ||
145 | + currentUser.getCustomerId()); | ||
146 | + return new BasicPluginWebsocketSessionRef(UUID.randomUUID().toString(), securityCtx, session.getUri(), session.getAttributes(), | ||
147 | + session.getLocalAddress(), session.getRemoteAddress()); | ||
148 | + } else { | ||
149 | + throw new SecurityException("Current user is not allowed to use this plugin!"); | ||
150 | + } | ||
151 | + } | ||
152 | + } | ||
153 | + | ||
154 | + private static class SessionMetaData { | ||
155 | + private final WebSocketSession session; | ||
156 | + private final PluginWebsocketSessionRef sessionRef; | ||
157 | + | ||
158 | + public SessionMetaData(WebSocketSession session, PluginWebsocketSessionRef sessionRef) { | ||
159 | + super(); | ||
160 | + this.session = session; | ||
161 | + this.sessionRef = sessionRef; | ||
162 | + } | ||
163 | + } | ||
164 | + | ||
165 | + @Override | ||
166 | + public void send(PluginWebsocketMsg<?> wsMsg) throws IOException { | ||
167 | + PluginWebsocketSessionRef sessionRef = wsMsg.getSessionRef(); | ||
168 | + String externalId = sessionRef.getSessionId(); | ||
169 | + log.debug("[{}] Processing {}", externalId, wsMsg); | ||
170 | + String internalId = externalSessionMap.get(externalId); | ||
171 | + if (internalId != null) { | ||
172 | + SessionMetaData sessionMd = internalSessionMap.get(internalId); | ||
173 | + if (sessionMd != null) { | ||
174 | + if (wsMsg instanceof TextPluginWebSocketMsg) { | ||
175 | + String payload = ((TextPluginWebSocketMsg) wsMsg).getPayload(); | ||
176 | + sessionMd.session.sendMessage(new TextMessage(payload)); | ||
177 | + } | ||
178 | + } else { | ||
179 | + log.warn("[{}][{}] Failed to find session by internal id", externalId, internalId); | ||
180 | + } | ||
181 | + } else { | ||
182 | + log.warn("[{}] Failed to find session by external id", externalId); | ||
183 | + } | ||
184 | + } | ||
185 | + | ||
186 | + @Override | ||
187 | + public void close(PluginWebsocketSessionRef sessionRef) throws IOException { | ||
188 | + String externalId = sessionRef.getSessionId(); | ||
189 | + log.debug("[{}] Processing close request", externalId); | ||
190 | + String internalId = externalSessionMap.get(externalId); | ||
191 | + if (internalId != null) { | ||
192 | + SessionMetaData sessionMd = internalSessionMap.get(internalId); | ||
193 | + if (sessionMd != null) { | ||
194 | + sessionMd.session.close(CloseStatus.NORMAL); | ||
195 | + } else { | ||
196 | + log.warn("[{}][{}] Failed to find session by internal id", externalId, internalId); | ||
197 | + } | ||
198 | + } else { | ||
199 | + log.warn("[{}] Failed to find session by external id", externalId); | ||
200 | + } | ||
201 | + } | ||
202 | + | ||
203 | +} |
application/src/main/java/org/thingsboard/server/controller/plugin/PluginWebSocketMsgEndpoint.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.controller.plugin; | ||
17 | + | ||
18 | +import java.io.IOException; | ||
19 | + | ||
20 | +import org.thingsboard.server.extensions.api.plugins.ws.PluginWebsocketSessionRef; | ||
21 | +import org.thingsboard.server.extensions.api.plugins.ws.msg.PluginWebsocketMsg; | ||
22 | + | ||
23 | +public interface PluginWebSocketMsgEndpoint { | ||
24 | + | ||
25 | + void send(PluginWebsocketMsg<?> wsMsg) throws IOException; | ||
26 | + | ||
27 | + void close(PluginWebsocketSessionRef sessionRef) throws IOException; | ||
28 | +} |
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.exception; | ||
17 | + | ||
18 | +import com.fasterxml.jackson.annotation.JsonValue; | ||
19 | + | ||
20 | +public enum ThingsboardErrorCode { | ||
21 | + | ||
22 | + GENERAL(2), | ||
23 | + AUTHENTICATION(10), | ||
24 | + JWT_TOKEN_EXPIRED(11), | ||
25 | + PERMISSION_DENIED(20), | ||
26 | + INVALID_ARGUMENTS(30), | ||
27 | + BAD_REQUEST_PARAMS(31), | ||
28 | + ITEM_NOT_FOUND(32); | ||
29 | + | ||
30 | + private int errorCode; | ||
31 | + | ||
32 | + ThingsboardErrorCode(int errorCode) { | ||
33 | + this.errorCode = errorCode; | ||
34 | + } | ||
35 | + | ||
36 | + @JsonValue | ||
37 | + public int getErrorCode() { | ||
38 | + return errorCode; | ||
39 | + } | ||
40 | + | ||
41 | +} |
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.exception; | ||
17 | + | ||
18 | +import org.springframework.http.HttpStatus; | ||
19 | + | ||
20 | +import java.util.Date; | ||
21 | + | ||
22 | +public class ThingsboardErrorResponse { | ||
23 | + // HTTP Response Status Code | ||
24 | + private final HttpStatus status; | ||
25 | + | ||
26 | + // General Error message | ||
27 | + private final String message; | ||
28 | + | ||
29 | + // Error code | ||
30 | + private final ThingsboardErrorCode errorCode; | ||
31 | + | ||
32 | + private final Date timestamp; | ||
33 | + | ||
34 | + protected ThingsboardErrorResponse(final String message, final ThingsboardErrorCode errorCode, HttpStatus status) { | ||
35 | + this.message = message; | ||
36 | + this.errorCode = errorCode; | ||
37 | + this.status = status; | ||
38 | + this.timestamp = new java.util.Date(); | ||
39 | + } | ||
40 | + | ||
41 | + public static ThingsboardErrorResponse of(final String message, final ThingsboardErrorCode errorCode, HttpStatus status) { | ||
42 | + return new ThingsboardErrorResponse(message, errorCode, status); | ||
43 | + } | ||
44 | + | ||
45 | + public Integer getStatus() { | ||
46 | + return status.value(); | ||
47 | + } | ||
48 | + | ||
49 | + public String getMessage() { | ||
50 | + return message; | ||
51 | + } | ||
52 | + | ||
53 | + public ThingsboardErrorCode getErrorCode() { | ||
54 | + return errorCode; | ||
55 | + } | ||
56 | + | ||
57 | + public Date getTimestamp() { | ||
58 | + return timestamp; | ||
59 | + } | ||
60 | +} |
application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.exception; | ||
17 | + | ||
18 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
19 | +import lombok.extern.slf4j.Slf4j; | ||
20 | +import org.slf4j.Logger; | ||
21 | +import org.slf4j.LoggerFactory; | ||
22 | +import org.springframework.beans.factory.annotation.Autowired; | ||
23 | +import org.springframework.http.HttpStatus; | ||
24 | +import org.springframework.http.MediaType; | ||
25 | +import org.springframework.security.access.AccessDeniedException; | ||
26 | +import org.springframework.security.authentication.BadCredentialsException; | ||
27 | +import org.springframework.security.core.AuthenticationException; | ||
28 | +import org.springframework.security.web.access.AccessDeniedHandler; | ||
29 | +import org.springframework.stereotype.Component; | ||
30 | +import org.thingsboard.server.service.security.exception.AuthMethodNotSupportedException; | ||
31 | +import org.thingsboard.server.service.security.exception.JwtExpiredTokenException; | ||
32 | + | ||
33 | +import javax.servlet.ServletException; | ||
34 | +import javax.servlet.http.HttpServletRequest; | ||
35 | +import javax.servlet.http.HttpServletResponse; | ||
36 | +import java.io.IOException; | ||
37 | +@Component | ||
38 | +@Slf4j | ||
39 | +public class ThingsboardErrorResponseHandler implements AccessDeniedHandler { | ||
40 | + | ||
41 | + @Autowired | ||
42 | + private ObjectMapper mapper; | ||
43 | + | ||
44 | + @Override | ||
45 | + public void handle(HttpServletRequest request, HttpServletResponse response, | ||
46 | + AccessDeniedException accessDeniedException) throws IOException, | ||
47 | + ServletException { | ||
48 | + if (!response.isCommitted()) { | ||
49 | + response.setContentType(MediaType.APPLICATION_JSON_VALUE); | ||
50 | + response.setStatus(HttpStatus.FORBIDDEN.value()); | ||
51 | + mapper.writeValue(response.getWriter(), | ||
52 | + ThingsboardErrorResponse.of("You don't have permission to perform this operation!", | ||
53 | + ThingsboardErrorCode.PERMISSION_DENIED, HttpStatus.FORBIDDEN)); | ||
54 | + } | ||
55 | + } | ||
56 | + | ||
57 | + public void handle(Exception exception, HttpServletResponse response) { | ||
58 | + log.debug("Processing exception {}", exception.getMessage(), exception); | ||
59 | + if (!response.isCommitted()) { | ||
60 | + try { | ||
61 | + response.setContentType(MediaType.APPLICATION_JSON_VALUE); | ||
62 | + | ||
63 | + if (exception instanceof ThingsboardException) { | ||
64 | + handleThingsboardException((ThingsboardException) exception, response); | ||
65 | + } else if (exception instanceof AccessDeniedException) { | ||
66 | + handleAccessDeniedException(response); | ||
67 | + } else if (exception instanceof AuthenticationException) { | ||
68 | + handleAuthenticationException((AuthenticationException) exception, response); | ||
69 | + } else { | ||
70 | + response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); | ||
71 | + mapper.writeValue(response.getWriter(), ThingsboardErrorResponse.of(exception.getMessage(), | ||
72 | + ThingsboardErrorCode.GENERAL, HttpStatus.INTERNAL_SERVER_ERROR)); | ||
73 | + } | ||
74 | + } catch (IOException e) { | ||
75 | + log.error("Can't handle exception", e); | ||
76 | + } | ||
77 | + } | ||
78 | + } | ||
79 | + | ||
80 | + private void handleThingsboardException(ThingsboardException thingsboardException, HttpServletResponse response) throws IOException { | ||
81 | + | ||
82 | + ThingsboardErrorCode errorCode = thingsboardException.getErrorCode(); | ||
83 | + HttpStatus status; | ||
84 | + | ||
85 | + switch (errorCode) { | ||
86 | + case AUTHENTICATION: | ||
87 | + status = HttpStatus.UNAUTHORIZED; | ||
88 | + break; | ||
89 | + case PERMISSION_DENIED: | ||
90 | + status = HttpStatus.FORBIDDEN; | ||
91 | + break; | ||
92 | + case INVALID_ARGUMENTS: | ||
93 | + status = HttpStatus.BAD_REQUEST; | ||
94 | + break; | ||
95 | + case ITEM_NOT_FOUND: | ||
96 | + status = HttpStatus.NOT_FOUND; | ||
97 | + break; | ||
98 | + case BAD_REQUEST_PARAMS: | ||
99 | + status = HttpStatus.BAD_REQUEST; | ||
100 | + break; | ||
101 | + case GENERAL: | ||
102 | + status = HttpStatus.INTERNAL_SERVER_ERROR; | ||
103 | + break; | ||
104 | + default: | ||
105 | + status = HttpStatus.INTERNAL_SERVER_ERROR; | ||
106 | + break; | ||
107 | + } | ||
108 | + | ||
109 | + response.setStatus(status.value()); | ||
110 | + mapper.writeValue(response.getWriter(), ThingsboardErrorResponse.of(thingsboardException.getMessage(), errorCode, status)); | ||
111 | + } | ||
112 | + | ||
113 | + private void handleAccessDeniedException(HttpServletResponse response) throws IOException { | ||
114 | + response.setStatus(HttpStatus.FORBIDDEN.value()); | ||
115 | + mapper.writeValue(response.getWriter(), | ||
116 | + ThingsboardErrorResponse.of("You don't have permission to perform this operation!", | ||
117 | + ThingsboardErrorCode.PERMISSION_DENIED, HttpStatus.FORBIDDEN)); | ||
118 | + | ||
119 | + } | ||
120 | + | ||
121 | + private void handleAuthenticationException(AuthenticationException authenticationException, HttpServletResponse response) throws IOException { | ||
122 | + response.setStatus(HttpStatus.UNAUTHORIZED.value()); | ||
123 | + if (authenticationException instanceof BadCredentialsException) { | ||
124 | + mapper.writeValue(response.getWriter(), ThingsboardErrorResponse.of("Invalid username or password", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)); | ||
125 | + } else if (authenticationException instanceof JwtExpiredTokenException) { | ||
126 | + mapper.writeValue(response.getWriter(), ThingsboardErrorResponse.of("Token has expired", ThingsboardErrorCode.JWT_TOKEN_EXPIRED, HttpStatus.UNAUTHORIZED)); | ||
127 | + } else if (authenticationException instanceof AuthMethodNotSupportedException) { | ||
128 | + mapper.writeValue(response.getWriter(), ThingsboardErrorResponse.of(authenticationException.getMessage(), ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)); | ||
129 | + } | ||
130 | + mapper.writeValue(response.getWriter(), ThingsboardErrorResponse.of("Authentication failed", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)); | ||
131 | + } | ||
132 | + | ||
133 | +} |
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.exception; | ||
17 | + | ||
18 | +public class ThingsboardException extends Exception { | ||
19 | + | ||
20 | + private static final long serialVersionUID = 1L; | ||
21 | + | ||
22 | + private ThingsboardErrorCode errorCode; | ||
23 | + | ||
24 | + public ThingsboardException() { | ||
25 | + super(); | ||
26 | + } | ||
27 | + | ||
28 | + public ThingsboardException(ThingsboardErrorCode errorCode) { | ||
29 | + this.errorCode = errorCode; | ||
30 | + } | ||
31 | + | ||
32 | + public ThingsboardException(String message, ThingsboardErrorCode errorCode) { | ||
33 | + super(message); | ||
34 | + this.errorCode = errorCode; | ||
35 | + } | ||
36 | + | ||
37 | + public ThingsboardException(String message, Throwable cause, ThingsboardErrorCode errorCode) { | ||
38 | + super(message, cause); | ||
39 | + this.errorCode = errorCode; | ||
40 | + } | ||
41 | + | ||
42 | + public ThingsboardException(Throwable cause, ThingsboardErrorCode errorCode) { | ||
43 | + super(cause); | ||
44 | + this.errorCode = errorCode; | ||
45 | + } | ||
46 | + | ||
47 | + public ThingsboardErrorCode getErrorCode() { | ||
48 | + return errorCode; | ||
49 | + } | ||
50 | + | ||
51 | +} |
application/src/main/java/org/thingsboard/server/service/environment/EnvironmentLogService.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.service.environment; | ||
17 | + | ||
18 | +import lombok.extern.slf4j.Slf4j; | ||
19 | +import org.apache.zookeeper.Environment; | ||
20 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | ||
21 | +import org.springframework.stereotype.Service; | ||
22 | + | ||
23 | +import javax.annotation.PostConstruct; | ||
24 | + | ||
25 | +/** | ||
26 | + * Created by igor on 11/24/16. | ||
27 | + */ | ||
28 | + | ||
29 | +@Service("environmentLogService") | ||
30 | +@ConditionalOnProperty(prefix = "zk", value = "enabled", havingValue = "false", matchIfMissing = true) | ||
31 | +@Slf4j | ||
32 | +public class EnvironmentLogService { | ||
33 | + | ||
34 | + @PostConstruct | ||
35 | + public void init() { | ||
36 | + Environment.logEnv("Thingsboard server environment: ", log); | ||
37 | + } | ||
38 | + | ||
39 | +} |
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.service.mail; | ||
17 | + | ||
18 | +import java.util.HashMap; | ||
19 | +import java.util.Locale; | ||
20 | +import java.util.Map; | ||
21 | +import java.util.Properties; | ||
22 | + | ||
23 | +import javax.annotation.PostConstruct; | ||
24 | +import javax.mail.internet.MimeMessage; | ||
25 | + | ||
26 | +import lombok.extern.slf4j.Slf4j; | ||
27 | +import org.apache.commons.lang3.StringUtils; | ||
28 | +import org.apache.velocity.app.VelocityEngine; | ||
29 | +import org.thingsboard.server.exception.ThingsboardErrorCode; | ||
30 | +import org.thingsboard.server.exception.ThingsboardException; | ||
31 | +import org.thingsboard.server.common.data.AdminSettings; | ||
32 | +import org.thingsboard.server.dao.settings.AdminSettingsService; | ||
33 | +import org.thingsboard.server.dao.exception.IncorrectParameterException; | ||
34 | +import org.slf4j.Logger; | ||
35 | +import org.slf4j.LoggerFactory; | ||
36 | +import org.springframework.beans.factory.annotation.Autowired; | ||
37 | +import org.springframework.context.MessageSource; | ||
38 | +import org.springframework.core.NestedRuntimeException; | ||
39 | +import org.springframework.mail.javamail.JavaMailSenderImpl; | ||
40 | +import org.springframework.mail.javamail.MimeMessageHelper; | ||
41 | +import org.springframework.stereotype.Service; | ||
42 | +import org.springframework.ui.velocity.VelocityEngineUtils; | ||
43 | + | ||
44 | +import com.fasterxml.jackson.databind.JsonNode; | ||
45 | +@Service | ||
46 | +@Slf4j | ||
47 | +public class DefaultMailService implements MailService { | ||
48 | + | ||
49 | + @Autowired | ||
50 | + private MessageSource messages; | ||
51 | + | ||
52 | + @Autowired | ||
53 | + private VelocityEngine engine; | ||
54 | + | ||
55 | + private JavaMailSenderImpl mailSender; | ||
56 | + | ||
57 | + private String mailFrom; | ||
58 | + | ||
59 | + @Autowired | ||
60 | + private AdminSettingsService adminSettingsService; | ||
61 | + | ||
62 | + @PostConstruct | ||
63 | + private void init() { | ||
64 | + updateMailConfiguration(); | ||
65 | + } | ||
66 | + | ||
67 | + @Override | ||
68 | + public void updateMailConfiguration() { | ||
69 | + AdminSettings settings = adminSettingsService.findAdminSettingsByKey("mail"); | ||
70 | + JsonNode jsonConfig = settings.getJsonValue(); | ||
71 | + mailSender = createMailSender(jsonConfig); | ||
72 | + mailFrom = jsonConfig.get("mailFrom").asText(); | ||
73 | + } | ||
74 | + | ||
75 | + private JavaMailSenderImpl createMailSender(JsonNode jsonConfig) { | ||
76 | + JavaMailSenderImpl mailSender = new JavaMailSenderImpl(); | ||
77 | + mailSender.setHost(jsonConfig.get("smtpHost").asText()); | ||
78 | + mailSender.setPort(parsePort(jsonConfig.get("smtpPort").asText())); | ||
79 | + mailSender.setUsername(jsonConfig.get("username").asText()); | ||
80 | + mailSender.setPassword(jsonConfig.get("password").asText()); | ||
81 | + mailSender.setJavaMailProperties(createJavaMailProperties(jsonConfig)); | ||
82 | + return mailSender; | ||
83 | + } | ||
84 | + | ||
85 | + private Properties createJavaMailProperties(JsonNode jsonConfig) { | ||
86 | + Properties javaMailProperties = new Properties(); | ||
87 | + String protocol = jsonConfig.get("smtpProtocol").asText(); | ||
88 | + javaMailProperties.put("mail.transport.protocol", protocol); | ||
89 | + javaMailProperties.put("mail." + protocol + ".host", jsonConfig.get("smtpHost").asText()); | ||
90 | + javaMailProperties.put("mail." + protocol + ".port", jsonConfig.get("smtpPort").asText()); | ||
91 | + javaMailProperties.put("mail." + protocol + ".timeout", jsonConfig.get("timeout").asText()); | ||
92 | + javaMailProperties.put("mail." + protocol + ".auth", String.valueOf(StringUtils.isNotEmpty(jsonConfig.get("username").asText()))); | ||
93 | + javaMailProperties.put("mail." + protocol + ".starttls.enable", jsonConfig.get("enableTls")); | ||
94 | + return javaMailProperties; | ||
95 | + } | ||
96 | + | ||
97 | + private int parsePort(String strPort) { | ||
98 | + try { | ||
99 | + return Integer.valueOf(strPort); | ||
100 | + } catch (NumberFormatException e) { | ||
101 | + throw new IncorrectParameterException(String.format("Invalid smtp port value: %s", strPort)); | ||
102 | + } | ||
103 | + } | ||
104 | + | ||
105 | + @Override | ||
106 | + public void sendTestMail(JsonNode jsonConfig, String email) throws ThingsboardException { | ||
107 | + JavaMailSenderImpl testMailSender = createMailSender(jsonConfig); | ||
108 | + String mailFrom = jsonConfig.get("mailFrom").asText(); | ||
109 | + String subject = messages.getMessage("test.message.subject", null, Locale.US); | ||
110 | + | ||
111 | + Map<String, Object> model = new HashMap<String, Object>(); | ||
112 | + model.put("targetEmail", email); | ||
113 | + | ||
114 | + String message = VelocityEngineUtils.mergeTemplateIntoString(this.engine, | ||
115 | + "test.vm", "UTF-8", model); | ||
116 | + | ||
117 | + sendMail(testMailSender, mailFrom, email, subject, message); | ||
118 | + } | ||
119 | + | ||
120 | + @Override | ||
121 | + public void sendActivationEmail(String activationLink, String email) throws ThingsboardException { | ||
122 | + | ||
123 | + String subject = messages.getMessage("activation.subject", null, Locale.US); | ||
124 | + | ||
125 | + Map<String, Object> model = new HashMap<String, Object>(); | ||
126 | + model.put("activationLink", activationLink); | ||
127 | + model.put("targetEmail", email); | ||
128 | + | ||
129 | + String message = VelocityEngineUtils.mergeTemplateIntoString(this.engine, | ||
130 | + "activation.vm", "UTF-8", model); | ||
131 | + | ||
132 | + sendMail(mailSender, mailFrom, email, subject, message); | ||
133 | + } | ||
134 | + | ||
135 | + @Override | ||
136 | + public void sendAccountActivatedEmail(String loginLink, String email) throws ThingsboardException { | ||
137 | + | ||
138 | + String subject = messages.getMessage("account.activated.subject", null, Locale.US); | ||
139 | + | ||
140 | + Map<String, Object> model = new HashMap<String, Object>(); | ||
141 | + model.put("loginLink", loginLink); | ||
142 | + model.put("targetEmail", email); | ||
143 | + | ||
144 | + String message = VelocityEngineUtils.mergeTemplateIntoString(this.engine, | ||
145 | + "account.activated.vm", "UTF-8", model); | ||
146 | + | ||
147 | + sendMail(mailSender, mailFrom, email, subject, message); | ||
148 | + } | ||
149 | + | ||
150 | + @Override | ||
151 | + public void sendResetPasswordEmail(String passwordResetLink, String email) throws ThingsboardException { | ||
152 | + | ||
153 | + String subject = messages.getMessage("reset.password.subject", null, Locale.US); | ||
154 | + | ||
155 | + Map<String, Object> model = new HashMap<String, Object>(); | ||
156 | + model.put("passwordResetLink", passwordResetLink); | ||
157 | + model.put("targetEmail", email); | ||
158 | + | ||
159 | + String message = VelocityEngineUtils.mergeTemplateIntoString(this.engine, | ||
160 | + "reset.password.vm", "UTF-8", model); | ||
161 | + | ||
162 | + sendMail(mailSender, mailFrom, email, subject, message); | ||
163 | + } | ||
164 | + | ||
165 | + @Override | ||
166 | + public void sendPasswordWasResetEmail(String loginLink, String email) throws ThingsboardException { | ||
167 | + | ||
168 | + String subject = messages.getMessage("password.was.reset.subject", null, Locale.US); | ||
169 | + | ||
170 | + Map<String, Object> model = new HashMap<String, Object>(); | ||
171 | + model.put("loginLink", loginLink); | ||
172 | + model.put("targetEmail", email); | ||
173 | + | ||
174 | + String message = VelocityEngineUtils.mergeTemplateIntoString(this.engine, | ||
175 | + "password.was.reset.vm", "UTF-8", model); | ||
176 | + | ||
177 | + sendMail(mailSender, mailFrom, email, subject, message); | ||
178 | + } | ||
179 | + | ||
180 | + | ||
181 | + private void sendMail(JavaMailSenderImpl mailSender, | ||
182 | + String mailFrom, String email, | ||
183 | + String subject, String message) throws ThingsboardException { | ||
184 | + try { | ||
185 | + MimeMessage mimeMsg = mailSender.createMimeMessage(); | ||
186 | + MimeMessageHelper helper = new MimeMessageHelper(mimeMsg, "UTF-8"); | ||
187 | + helper.setFrom(mailFrom); | ||
188 | + helper.setTo(email); | ||
189 | + helper.setSubject(subject); | ||
190 | + helper.setText(message, true); | ||
191 | + mailSender.send(helper.getMimeMessage()); | ||
192 | + } catch (Exception e) { | ||
193 | + throw handleException(e); | ||
194 | + } | ||
195 | + } | ||
196 | + | ||
197 | + protected ThingsboardException handleException(Exception exception) { | ||
198 | + String message; | ||
199 | + if (exception instanceof NestedRuntimeException) { | ||
200 | + message = ((NestedRuntimeException)exception).getMostSpecificCause().getMessage(); | ||
201 | + } else { | ||
202 | + message = exception.getMessage(); | ||
203 | + } | ||
204 | + return new ThingsboardException(String.format("Unable to send mail: %s", message), | ||
205 | + ThingsboardErrorCode.GENERAL); | ||
206 | + } | ||
207 | + | ||
208 | +} |
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.service.mail; | ||
17 | + | ||
18 | +import org.thingsboard.server.exception.ThingsboardException; | ||
19 | + | ||
20 | +import com.fasterxml.jackson.databind.JsonNode; | ||
21 | + | ||
22 | +public interface MailService { | ||
23 | + | ||
24 | + void updateMailConfiguration(); | ||
25 | + | ||
26 | + void sendTestMail(JsonNode config, String email) throws ThingsboardException; | ||
27 | + | ||
28 | + void sendActivationEmail(String activationLink, String email) throws ThingsboardException; | ||
29 | + | ||
30 | + void sendAccountActivatedEmail(String loginLink, String email) throws ThingsboardException; | ||
31 | + | ||
32 | + void sendResetPasswordEmail(String passwordResetLink, String email) throws ThingsboardException; | ||
33 | + | ||
34 | + void sendPasswordWasResetEmail(String loginLink, String email) throws ThingsboardException; | ||
35 | + | ||
36 | +} |
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.service.security.auth; | ||
17 | + | ||
18 | +import org.springframework.security.authentication.AbstractAuthenticationToken; | ||
19 | +import org.thingsboard.server.service.security.model.SecurityUser; | ||
20 | +import org.thingsboard.server.service.security.model.token.RawAccessJwtToken; | ||
21 | + | ||
22 | +public abstract class AbstractJwtAuthenticationToken extends AbstractAuthenticationToken { | ||
23 | + | ||
24 | + private static final long serialVersionUID = -6212297506742428406L; | ||
25 | + | ||
26 | + private RawAccessJwtToken rawAccessToken; | ||
27 | + private SecurityUser securityUser; | ||
28 | + | ||
29 | + public AbstractJwtAuthenticationToken(RawAccessJwtToken unsafeToken) { | ||
30 | + super(null); | ||
31 | + this.rawAccessToken = unsafeToken; | ||
32 | + this.setAuthenticated(false); | ||
33 | + } | ||
34 | + | ||
35 | + public AbstractJwtAuthenticationToken(SecurityUser securityUser) { | ||
36 | + super(securityUser.getAuthorities()); | ||
37 | + this.eraseCredentials(); | ||
38 | + this.securityUser = securityUser; | ||
39 | + super.setAuthenticated(true); | ||
40 | + } | ||
41 | + | ||
42 | + @Override | ||
43 | + public void setAuthenticated(boolean authenticated) { | ||
44 | + if (authenticated) { | ||
45 | + throw new IllegalArgumentException( | ||
46 | + "Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead"); | ||
47 | + } | ||
48 | + super.setAuthenticated(false); | ||
49 | + } | ||
50 | + | ||
51 | + @Override | ||
52 | + public Object getCredentials() { | ||
53 | + return rawAccessToken; | ||
54 | + } | ||
55 | + | ||
56 | + @Override | ||
57 | + public Object getPrincipal() { | ||
58 | + return this.securityUser; | ||
59 | + } | ||
60 | + | ||
61 | + @Override | ||
62 | + public void eraseCredentials() { | ||
63 | + super.eraseCredentials(); | ||
64 | + this.rawAccessToken = null; | ||
65 | + } | ||
66 | +} |
application/src/main/java/org/thingsboard/server/service/security/auth/JwtAuthenticationToken.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.service.security.auth; | ||
17 | + | ||
18 | +import org.thingsboard.server.service.security.model.SecurityUser; | ||
19 | +import org.thingsboard.server.service.security.model.token.RawAccessJwtToken; | ||
20 | + | ||
21 | +public class JwtAuthenticationToken extends AbstractJwtAuthenticationToken { | ||
22 | + | ||
23 | + private static final long serialVersionUID = -8487219769037942225L; | ||
24 | + | ||
25 | + public JwtAuthenticationToken(RawAccessJwtToken unsafeToken) { | ||
26 | + super(unsafeToken); | ||
27 | + } | ||
28 | + | ||
29 | + public JwtAuthenticationToken(SecurityUser securityUser) { | ||
30 | + super(securityUser); | ||
31 | + } | ||
32 | +} |
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.service.security.auth; | ||
17 | + | ||
18 | +import org.thingsboard.server.service.security.model.SecurityUser; | ||
19 | +import org.thingsboard.server.service.security.model.token.RawAccessJwtToken; | ||
20 | + | ||
21 | +public class RefreshAuthenticationToken extends AbstractJwtAuthenticationToken { | ||
22 | + | ||
23 | + private static final long serialVersionUID = -1311042791508924523L; | ||
24 | + | ||
25 | + public RefreshAuthenticationToken(RawAccessJwtToken unsafeToken) { | ||
26 | + super(unsafeToken); | ||
27 | + } | ||
28 | + | ||
29 | + public RefreshAuthenticationToken(SecurityUser securityUser) { | ||
30 | + super(securityUser); | ||
31 | + } | ||
32 | +} |
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.service.security.auth.jwt; | ||
17 | + | ||
18 | +import io.jsonwebtoken.Claims; | ||
19 | +import io.jsonwebtoken.Jws; | ||
20 | +import org.springframework.beans.factory.annotation.Autowired; | ||
21 | +import org.springframework.security.authentication.AuthenticationProvider; | ||
22 | +import org.springframework.security.core.Authentication; | ||
23 | +import org.springframework.security.core.AuthenticationException; | ||
24 | +import org.springframework.security.core.GrantedAuthority; | ||
25 | +import org.springframework.security.core.authority.SimpleGrantedAuthority; | ||
26 | +import org.springframework.stereotype.Component; | ||
27 | +import org.thingsboard.server.config.JwtSettings; | ||
28 | +import org.thingsboard.server.service.security.auth.JwtAuthenticationToken; | ||
29 | +import org.thingsboard.server.service.security.model.SecurityUser; | ||
30 | +import org.thingsboard.server.service.security.model.token.JwtTokenFactory; | ||
31 | +import org.thingsboard.server.service.security.model.token.RawAccessJwtToken; | ||
32 | + | ||
33 | +import java.util.List; | ||
34 | +import java.util.stream.Collectors; | ||
35 | + | ||
36 | +@Component | ||
37 | +@SuppressWarnings("unchecked") | ||
38 | +public class JwtAuthenticationProvider implements AuthenticationProvider { | ||
39 | + | ||
40 | + private final JwtTokenFactory tokenFactory; | ||
41 | + | ||
42 | + @Autowired | ||
43 | + public JwtAuthenticationProvider(JwtTokenFactory tokenFactory) { | ||
44 | + this.tokenFactory = tokenFactory; | ||
45 | + } | ||
46 | + | ||
47 | + @Override | ||
48 | + public Authentication authenticate(Authentication authentication) throws AuthenticationException { | ||
49 | + RawAccessJwtToken rawAccessToken = (RawAccessJwtToken) authentication.getCredentials(); | ||
50 | + SecurityUser securityUser = tokenFactory.parseAccessJwtToken(rawAccessToken); | ||
51 | + return new JwtAuthenticationToken(securityUser); | ||
52 | + } | ||
53 | + | ||
54 | + @Override | ||
55 | + public boolean supports(Class<?> authentication) { | ||
56 | + return (JwtAuthenticationToken.class.isAssignableFrom(authentication)); | ||
57 | + } | ||
58 | +} |
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.service.security.auth.jwt; | ||
17 | + | ||
18 | +import org.springframework.beans.factory.annotation.Autowired; | ||
19 | +import org.springframework.security.core.Authentication; | ||
20 | +import org.springframework.security.core.AuthenticationException; | ||
21 | +import org.springframework.security.core.context.SecurityContext; | ||
22 | +import org.springframework.security.core.context.SecurityContextHolder; | ||
23 | +import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; | ||
24 | +import org.springframework.security.web.authentication.AuthenticationFailureHandler; | ||
25 | +import org.springframework.security.web.util.matcher.RequestMatcher; | ||
26 | +import org.thingsboard.server.config.ThingsboardSecurityConfiguration; | ||
27 | +import org.thingsboard.server.service.security.auth.JwtAuthenticationToken; | ||
28 | +import org.thingsboard.server.service.security.auth.jwt.extractor.TokenExtractor; | ||
29 | +import org.thingsboard.server.service.security.model.token.RawAccessJwtToken; | ||
30 | + | ||
31 | +import javax.servlet.FilterChain; | ||
32 | +import javax.servlet.ServletException; | ||
33 | +import javax.servlet.http.HttpServletRequest; | ||
34 | +import javax.servlet.http.HttpServletResponse; | ||
35 | +import java.io.IOException; | ||
36 | + | ||
37 | +public class JwtTokenAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter { | ||
38 | + private final AuthenticationFailureHandler failureHandler; | ||
39 | + private final TokenExtractor tokenExtractor; | ||
40 | + | ||
41 | + @Autowired | ||
42 | + public JwtTokenAuthenticationProcessingFilter(AuthenticationFailureHandler failureHandler, | ||
43 | + TokenExtractor tokenExtractor, RequestMatcher matcher) { | ||
44 | + super(matcher); | ||
45 | + this.failureHandler = failureHandler; | ||
46 | + this.tokenExtractor = tokenExtractor; | ||
47 | + } | ||
48 | + | ||
49 | + @Override | ||
50 | + public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) | ||
51 | + throws AuthenticationException, IOException, ServletException { | ||
52 | + RawAccessJwtToken token = new RawAccessJwtToken(tokenExtractor.extract(request)); | ||
53 | + return getAuthenticationManager().authenticate(new JwtAuthenticationToken(token)); | ||
54 | + } | ||
55 | + | ||
56 | + @Override | ||
57 | + protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, | ||
58 | + Authentication authResult) throws IOException, ServletException { | ||
59 | + SecurityContext context = SecurityContextHolder.createEmptyContext(); | ||
60 | + context.setAuthentication(authResult); | ||
61 | + SecurityContextHolder.setContext(context); | ||
62 | + chain.doFilter(request, response); | ||
63 | + } | ||
64 | + | ||
65 | + @Override | ||
66 | + protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, | ||
67 | + AuthenticationException failed) throws IOException, ServletException { | ||
68 | + SecurityContextHolder.clearContext(); | ||
69 | + failureHandler.onAuthenticationFailure(request, response, failed); | ||
70 | + } | ||
71 | +} |
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.service.security.auth.jwt; | ||
17 | + | ||
18 | +import org.springframework.beans.factory.annotation.Autowired; | ||
19 | +import org.springframework.security.authentication.AuthenticationProvider; | ||
20 | +import org.springframework.security.authentication.DisabledException; | ||
21 | +import org.springframework.security.authentication.InsufficientAuthenticationException; | ||
22 | +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | ||
23 | +import org.springframework.security.core.Authentication; | ||
24 | +import org.springframework.security.core.AuthenticationException; | ||
25 | +import org.springframework.security.core.userdetails.UsernameNotFoundException; | ||
26 | +import org.springframework.stereotype.Component; | ||
27 | +import org.springframework.util.Assert; | ||
28 | +import org.thingsboard.server.common.data.User; | ||
29 | +import org.thingsboard.server.common.data.security.UserCredentials; | ||
30 | +import org.thingsboard.server.dao.user.UserService; | ||
31 | +import org.thingsboard.server.service.security.auth.RefreshAuthenticationToken; | ||
32 | +import org.thingsboard.server.service.security.model.SecurityUser; | ||
33 | +import org.thingsboard.server.service.security.model.token.JwtTokenFactory; | ||
34 | +import org.thingsboard.server.service.security.model.token.RawAccessJwtToken; | ||
35 | + | ||
36 | +@Component | ||
37 | +public class RefreshTokenAuthenticationProvider implements AuthenticationProvider { | ||
38 | + | ||
39 | + private final JwtTokenFactory tokenFactory; | ||
40 | + private final UserService userService; | ||
41 | + | ||
42 | + @Autowired | ||
43 | + public RefreshTokenAuthenticationProvider(final UserService userService, final JwtTokenFactory tokenFactory) { | ||
44 | + this.userService = userService; | ||
45 | + this.tokenFactory = tokenFactory; | ||
46 | + } | ||
47 | + | ||
48 | + @Override | ||
49 | + public Authentication authenticate(Authentication authentication) throws AuthenticationException { | ||
50 | + Assert.notNull(authentication, "No authentication data provided"); | ||
51 | + RawAccessJwtToken rawAccessToken = (RawAccessJwtToken) authentication.getCredentials(); | ||
52 | + SecurityUser unsafeUser = tokenFactory.parseRefreshToken(rawAccessToken); | ||
53 | + | ||
54 | + User user = userService.findUserById(unsafeUser.getId()); | ||
55 | + if (user == null) { | ||
56 | + throw new UsernameNotFoundException("User not found by refresh token"); | ||
57 | + } | ||
58 | + | ||
59 | + UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getId()); | ||
60 | + if (userCredentials == null) { | ||
61 | + throw new UsernameNotFoundException("User credentials not found"); | ||
62 | + } | ||
63 | + | ||
64 | + if (!userCredentials.isEnabled()) { | ||
65 | + throw new DisabledException("User is not active"); | ||
66 | + } | ||
67 | + | ||
68 | + if (user.getAuthority() == null) throw new InsufficientAuthenticationException("User has no authority assigned"); | ||
69 | + | ||
70 | + SecurityUser securityUser = new SecurityUser(user, userCredentials.isEnabled()); | ||
71 | + | ||
72 | + return new RefreshAuthenticationToken(securityUser); | ||
73 | + } | ||
74 | + | ||
75 | + @Override | ||
76 | + public boolean supports(Class<?> authentication) { | ||
77 | + return (RefreshAuthenticationToken.class.isAssignableFrom(authentication)); | ||
78 | + } | ||
79 | +} |
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.service.security.auth.jwt; | ||
17 | + | ||
18 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
19 | +import org.apache.commons.lang3.StringUtils; | ||
20 | +import org.slf4j.Logger; | ||
21 | +import org.slf4j.LoggerFactory; | ||
22 | +import org.springframework.http.HttpMethod; | ||
23 | +import org.springframework.security.authentication.AuthenticationServiceException; | ||
24 | +import org.springframework.security.core.Authentication; | ||
25 | +import org.springframework.security.core.AuthenticationException; | ||
26 | +import org.springframework.security.core.context.SecurityContextHolder; | ||
27 | +import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; | ||
28 | +import org.springframework.security.web.authentication.AuthenticationFailureHandler; | ||
29 | +import org.springframework.security.web.authentication.AuthenticationSuccessHandler; | ||
30 | +import org.thingsboard.server.service.security.auth.RefreshAuthenticationToken; | ||
31 | +import org.thingsboard.server.service.security.exception.AuthMethodNotSupportedException; | ||
32 | +import org.thingsboard.server.service.security.model.token.RawAccessJwtToken; | ||
33 | + | ||
34 | +import javax.servlet.FilterChain; | ||
35 | +import javax.servlet.ServletException; | ||
36 | +import javax.servlet.http.HttpServletRequest; | ||
37 | +import javax.servlet.http.HttpServletResponse; | ||
38 | +import java.io.IOException; | ||
39 | + | ||
40 | +public class RefreshTokenProcessingFilter extends AbstractAuthenticationProcessingFilter { | ||
41 | + private static Logger logger = LoggerFactory.getLogger(RefreshTokenProcessingFilter.class); | ||
42 | + | ||
43 | + private final AuthenticationSuccessHandler successHandler; | ||
44 | + private final AuthenticationFailureHandler failureHandler; | ||
45 | + | ||
46 | + private final ObjectMapper objectMapper; | ||
47 | + | ||
48 | + public RefreshTokenProcessingFilter(String defaultProcessUrl, AuthenticationSuccessHandler successHandler, | ||
49 | + AuthenticationFailureHandler failureHandler, ObjectMapper mapper) { | ||
50 | + super(defaultProcessUrl); | ||
51 | + this.successHandler = successHandler; | ||
52 | + this.failureHandler = failureHandler; | ||
53 | + this.objectMapper = mapper; | ||
54 | + } | ||
55 | + | ||
56 | + @Override | ||
57 | + public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) | ||
58 | + throws AuthenticationException, IOException, ServletException { | ||
59 | + if (!HttpMethod.POST.name().equals(request.getMethod())) { | ||
60 | + if(logger.isDebugEnabled()) { | ||
61 | + logger.debug("Authentication method not supported. Request method: " + request.getMethod()); | ||
62 | + } | ||
63 | + throw new AuthMethodNotSupportedException("Authentication method not supported"); | ||
64 | + } | ||
65 | + | ||
66 | + RefreshTokenRequest refreshTokenRequest; | ||
67 | + try { | ||
68 | + refreshTokenRequest = objectMapper.readValue(request.getReader(), RefreshTokenRequest.class); | ||
69 | + } catch (Exception e) { | ||
70 | + throw new AuthenticationServiceException("Invalid refresh token request payload"); | ||
71 | + } | ||
72 | + | ||
73 | + if (StringUtils.isBlank(refreshTokenRequest.getRefreshToken())) { | ||
74 | + throw new AuthenticationServiceException("Refresh token is not provided"); | ||
75 | + } | ||
76 | + | ||
77 | + RawAccessJwtToken token = new RawAccessJwtToken(refreshTokenRequest.getRefreshToken()); | ||
78 | + | ||
79 | + return this.getAuthenticationManager().authenticate(new RefreshAuthenticationToken(token)); | ||
80 | + } | ||
81 | + | ||
82 | + @Override | ||
83 | + protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, | ||
84 | + Authentication authResult) throws IOException, ServletException { | ||
85 | + successHandler.onAuthenticationSuccess(request, response, authResult); | ||
86 | + } | ||
87 | + | ||
88 | + @Override | ||
89 | + protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, | ||
90 | + AuthenticationException failed) throws IOException, ServletException { | ||
91 | + SecurityContextHolder.clearContext(); | ||
92 | + failureHandler.onAuthenticationFailure(request, response, failed); | ||
93 | + } | ||
94 | +} |
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.service.security.auth.jwt; | ||
17 | + | ||
18 | +import org.springframework.beans.factory.annotation.Autowired; | ||
19 | +import org.springframework.stereotype.Component; | ||
20 | +import org.thingsboard.server.service.security.model.SecurityUser; | ||
21 | +import org.thingsboard.server.service.security.model.token.JwtToken; | ||
22 | +import org.thingsboard.server.service.security.model.token.JwtTokenFactory; | ||
23 | + | ||
24 | +@Component | ||
25 | +public class RefreshTokenRepository { | ||
26 | + | ||
27 | + private final JwtTokenFactory tokenFactory; | ||
28 | + | ||
29 | + @Autowired | ||
30 | + public RefreshTokenRepository(final JwtTokenFactory tokenFactory) { | ||
31 | + this.tokenFactory = tokenFactory; | ||
32 | + } | ||
33 | + | ||
34 | + public JwtToken requestRefreshToken(SecurityUser user) { | ||
35 | + return tokenFactory.createRefreshToken(user); | ||
36 | + } | ||
37 | + | ||
38 | +} |
application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenRequest.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.service.security.auth.jwt; | ||
17 | + | ||
18 | +import com.fasterxml.jackson.annotation.JsonCreator; | ||
19 | +import com.fasterxml.jackson.annotation.JsonProperty; | ||
20 | + | ||
21 | +public class RefreshTokenRequest { | ||
22 | + private String refreshToken; | ||
23 | + | ||
24 | + @JsonCreator | ||
25 | + public RefreshTokenRequest(@JsonProperty("refreshToken") String refreshToken) { | ||
26 | + this.refreshToken = refreshToken; | ||
27 | + } | ||
28 | + | ||
29 | + public String getRefreshToken() { | ||
30 | + return refreshToken; | ||
31 | + } | ||
32 | +} |
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.service.security.auth.jwt; | ||
17 | + | ||
18 | +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; | ||
19 | +import org.springframework.security.web.util.matcher.OrRequestMatcher; | ||
20 | +import org.springframework.security.web.util.matcher.RequestMatcher; | ||
21 | +import org.springframework.util.Assert; | ||
22 | + | ||
23 | +import javax.servlet.http.HttpServletRequest; | ||
24 | +import java.util.List; | ||
25 | +import java.util.stream.Collectors; | ||
26 | + | ||
27 | +public class SkipPathRequestMatcher implements RequestMatcher { | ||
28 | + private OrRequestMatcher matchers; | ||
29 | + private RequestMatcher processingMatcher; | ||
30 | + | ||
31 | + public SkipPathRequestMatcher(List<String> pathsToSkip, String processingPath) { | ||
32 | + Assert.notNull(pathsToSkip); | ||
33 | + List<RequestMatcher> m = pathsToSkip.stream().map(path -> new AntPathRequestMatcher(path)).collect(Collectors.toList()); | ||
34 | + matchers = new OrRequestMatcher(m); | ||
35 | + processingMatcher = new AntPathRequestMatcher(processingPath); | ||
36 | + } | ||
37 | + | ||
38 | + @Override | ||
39 | + public boolean matches(HttpServletRequest request) { | ||
40 | + if (matchers.matches(request)) { | ||
41 | + return false; | ||
42 | + } | ||
43 | + return processingMatcher.matches(request) ? true : false; | ||
44 | + } | ||
45 | +} |
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.service.security.auth.jwt.extractor; | ||
17 | + | ||
18 | +import org.apache.commons.lang3.StringUtils; | ||
19 | +import org.springframework.security.authentication.AuthenticationServiceException; | ||
20 | +import org.springframework.stereotype.Component; | ||
21 | +import org.thingsboard.server.config.ThingsboardSecurityConfiguration; | ||
22 | + | ||
23 | +import javax.servlet.http.HttpServletRequest; | ||
24 | + | ||
25 | +@Component(value="jwtHeaderTokenExtractor") | ||
26 | +public class JwtHeaderTokenExtractor implements TokenExtractor { | ||
27 | + public static String HEADER_PREFIX = "Bearer "; | ||
28 | + | ||
29 | + @Override | ||
30 | + public String extract(HttpServletRequest request) { | ||
31 | + String header = request.getHeader(ThingsboardSecurityConfiguration.JWT_TOKEN_HEADER_PARAM); | ||
32 | + if (StringUtils.isBlank(header)) { | ||
33 | + throw new AuthenticationServiceException("Authorization header cannot be blank!"); | ||
34 | + } | ||
35 | + | ||
36 | + if (header.length() < HEADER_PREFIX.length()) { | ||
37 | + throw new AuthenticationServiceException("Invalid authorization header size."); | ||
38 | + } | ||
39 | + | ||
40 | + return header.substring(HEADER_PREFIX.length(), header.length()); | ||
41 | + } | ||
42 | +} |
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.service.security.auth.jwt.extractor; | ||
17 | + | ||
18 | +import org.apache.commons.lang3.StringUtils; | ||
19 | +import org.springframework.security.authentication.AuthenticationServiceException; | ||
20 | +import org.springframework.stereotype.Component; | ||
21 | +import org.thingsboard.server.config.ThingsboardSecurityConfiguration; | ||
22 | + | ||
23 | +import javax.servlet.http.HttpServletRequest; | ||
24 | + | ||
25 | +@Component(value="jwtQueryTokenExtractor") | ||
26 | +public class JwtQueryTokenExtractor implements TokenExtractor { | ||
27 | + | ||
28 | + @Override | ||
29 | + public String extract(HttpServletRequest request) { | ||
30 | + String token = null; | ||
31 | + if (request.getParameterMap() != null && !request.getParameterMap().isEmpty()) { | ||
32 | + String[] tokenParamValue = request.getParameterMap().get(ThingsboardSecurityConfiguration.JWT_TOKEN_QUERY_PARAM); | ||
33 | + if (tokenParamValue != null && tokenParamValue.length == 1) { | ||
34 | + token = tokenParamValue[0]; | ||
35 | + } | ||
36 | + } | ||
37 | + if (StringUtils.isBlank(token)) { | ||
38 | + throw new AuthenticationServiceException("Authorization query parameter cannot be blank!"); | ||
39 | + } | ||
40 | + | ||
41 | + return token; | ||
42 | + } | ||
43 | +} |
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.service.security.auth.jwt.extractor; | ||
17 | + | ||
18 | +import javax.servlet.http.HttpServletRequest; | ||
19 | + | ||
20 | +public interface TokenExtractor { | ||
21 | + public String extract(HttpServletRequest request); | ||
22 | +} |
application/src/main/java/org/thingsboard/server/service/security/auth/rest/LoginRequest.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.service.security.auth.rest; | ||
17 | + | ||
18 | +import com.fasterxml.jackson.annotation.JsonCreator; | ||
19 | +import com.fasterxml.jackson.annotation.JsonProperty; | ||
20 | + | ||
21 | +public class LoginRequest { | ||
22 | + private String username; | ||
23 | + private String password; | ||
24 | + | ||
25 | + @JsonCreator | ||
26 | + public LoginRequest(@JsonProperty("username") String username, @JsonProperty("password") String password) { | ||
27 | + this.username = username; | ||
28 | + this.password = password; | ||
29 | + } | ||
30 | + | ||
31 | + public String getUsername() { | ||
32 | + return username; | ||
33 | + } | ||
34 | + | ||
35 | + public String getPassword() { | ||
36 | + return password; | ||
37 | + } | ||
38 | +} |
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.service.security.auth.rest; | ||
17 | + | ||
18 | +import org.springframework.beans.factory.annotation.Autowired; | ||
19 | +import org.springframework.security.authentication.*; | ||
20 | +import org.springframework.security.core.Authentication; | ||
21 | +import org.springframework.security.core.AuthenticationException; | ||
22 | +import org.springframework.security.core.userdetails.UsernameNotFoundException; | ||
23 | +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; | ||
24 | +import org.springframework.stereotype.Component; | ||
25 | +import org.springframework.util.Assert; | ||
26 | +import org.thingsboard.server.common.data.User; | ||
27 | +import org.thingsboard.server.common.data.security.UserCredentials; | ||
28 | +import org.thingsboard.server.dao.user.UserService; | ||
29 | +import org.thingsboard.server.service.security.model.SecurityUser; | ||
30 | + | ||
31 | +@Component | ||
32 | +public class RestAuthenticationProvider implements AuthenticationProvider { | ||
33 | + | ||
34 | + private final BCryptPasswordEncoder encoder; | ||
35 | + private final UserService userService; | ||
36 | + | ||
37 | + @Autowired | ||
38 | + public RestAuthenticationProvider(final UserService userService, final BCryptPasswordEncoder encoder) { | ||
39 | + this.userService = userService; | ||
40 | + this.encoder = encoder; | ||
41 | + } | ||
42 | + | ||
43 | + @Override | ||
44 | + public Authentication authenticate(Authentication authentication) throws AuthenticationException { | ||
45 | + Assert.notNull(authentication, "No authentication data provided"); | ||
46 | + | ||
47 | + String username = (String) authentication.getPrincipal(); | ||
48 | + String password = (String) authentication.getCredentials(); | ||
49 | + | ||
50 | + User user = userService.findUserByEmail(username); | ||
51 | + if (user == null) { | ||
52 | + throw new UsernameNotFoundException("User not found: " + username); | ||
53 | + } | ||
54 | + | ||
55 | + UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getId()); | ||
56 | + if (userCredentials == null) { | ||
57 | + throw new UsernameNotFoundException("User credentials not found"); | ||
58 | + } | ||
59 | + | ||
60 | + if (!userCredentials.isEnabled()) { | ||
61 | + throw new DisabledException("User is not active"); | ||
62 | + } | ||
63 | + | ||
64 | + if (!encoder.matches(password, userCredentials.getPassword())) { | ||
65 | + throw new BadCredentialsException("Authentication Failed. Username or Password not valid."); | ||
66 | + } | ||
67 | + | ||
68 | + if (user.getAuthority() == null) throw new InsufficientAuthenticationException("User has no authority assigned"); | ||
69 | + | ||
70 | + SecurityUser securityUser = new SecurityUser(user, userCredentials.isEnabled()); | ||
71 | + | ||
72 | + return new UsernamePasswordAuthenticationToken(securityUser, null, securityUser.getAuthorities()); | ||
73 | + } | ||
74 | + | ||
75 | + @Override | ||
76 | + public boolean supports(Class<?> authentication) { | ||
77 | + return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication)); | ||
78 | + } | ||
79 | +} |
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.service.security.auth.rest; | ||
17 | + | ||
18 | +import org.springframework.beans.factory.annotation.Autowired; | ||
19 | +import org.springframework.security.core.AuthenticationException; | ||
20 | +import org.springframework.security.web.authentication.AuthenticationFailureHandler; | ||
21 | +import org.springframework.stereotype.Component; | ||
22 | +import org.thingsboard.server.exception.ThingsboardErrorResponseHandler; | ||
23 | + | ||
24 | +import javax.servlet.ServletException; | ||
25 | +import javax.servlet.http.HttpServletRequest; | ||
26 | +import javax.servlet.http.HttpServletResponse; | ||
27 | +import java.io.IOException; | ||
28 | + | ||
29 | +@Component | ||
30 | +public class RestAwareAuthenticationFailureHandler implements AuthenticationFailureHandler { | ||
31 | + | ||
32 | + private final ThingsboardErrorResponseHandler errorResponseHandler; | ||
33 | + | ||
34 | + @Autowired | ||
35 | + public RestAwareAuthenticationFailureHandler(ThingsboardErrorResponseHandler errorResponseHandler) { | ||
36 | + this.errorResponseHandler = errorResponseHandler; | ||
37 | + } | ||
38 | + | ||
39 | + @Override | ||
40 | + public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, | ||
41 | + AuthenticationException e) throws IOException, ServletException { | ||
42 | + errorResponseHandler.handle(e, response); | ||
43 | + } | ||
44 | +} |
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.service.security.auth.rest; | ||
17 | + | ||
18 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
19 | +import org.springframework.beans.factory.annotation.Autowired; | ||
20 | +import org.springframework.http.HttpStatus; | ||
21 | +import org.springframework.http.MediaType; | ||
22 | +import org.springframework.security.core.Authentication; | ||
23 | +import org.springframework.security.web.WebAttributes; | ||
24 | +import org.springframework.security.web.authentication.AuthenticationSuccessHandler; | ||
25 | +import org.springframework.stereotype.Component; | ||
26 | +import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository; | ||
27 | +import org.thingsboard.server.service.security.model.SecurityUser; | ||
28 | +import org.thingsboard.server.service.security.model.token.JwtToken; | ||
29 | +import org.thingsboard.server.service.security.model.token.JwtTokenFactory; | ||
30 | + | ||
31 | +import javax.servlet.ServletException; | ||
32 | +import javax.servlet.http.HttpServletRequest; | ||
33 | +import javax.servlet.http.HttpServletResponse; | ||
34 | +import javax.servlet.http.HttpSession; | ||
35 | +import java.io.IOException; | ||
36 | +import java.util.HashMap; | ||
37 | +import java.util.Map; | ||
38 | + | ||
39 | +@Component | ||
40 | +public class RestAwareAuthenticationSuccessHandler implements AuthenticationSuccessHandler { | ||
41 | + private final ObjectMapper mapper; | ||
42 | + private final JwtTokenFactory tokenFactory; | ||
43 | + private final RefreshTokenRepository refreshTokenRepository; | ||
44 | + | ||
45 | + @Autowired | ||
46 | + public RestAwareAuthenticationSuccessHandler(final ObjectMapper mapper, final JwtTokenFactory tokenFactory, final RefreshTokenRepository refreshTokenRepository) { | ||
47 | + this.mapper = mapper; | ||
48 | + this.tokenFactory = tokenFactory; | ||
49 | + this.refreshTokenRepository = refreshTokenRepository; | ||
50 | + } | ||
51 | + | ||
52 | + @Override | ||
53 | + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, | ||
54 | + Authentication authentication) throws IOException, ServletException { | ||
55 | + SecurityUser securityUser = (SecurityUser) authentication.getPrincipal(); | ||
56 | + | ||
57 | + JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser); | ||
58 | + JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser); | ||
59 | + | ||
60 | + Map<String, String> tokenMap = new HashMap<String, String>(); | ||
61 | + tokenMap.put("token", accessToken.getToken()); | ||
62 | + tokenMap.put("refreshToken", refreshToken.getToken()); | ||
63 | + | ||
64 | + response.setStatus(HttpStatus.OK.value()); | ||
65 | + response.setContentType(MediaType.APPLICATION_JSON_VALUE); | ||
66 | + mapper.writeValue(response.getWriter(), tokenMap); | ||
67 | + | ||
68 | + clearAuthenticationAttributes(request); | ||
69 | + } | ||
70 | + | ||
71 | + /** | ||
72 | + * Removes temporary authentication-related data which may have been stored | ||
73 | + * in the session during the authentication process.. | ||
74 | + * | ||
75 | + */ | ||
76 | + protected final void clearAuthenticationAttributes(HttpServletRequest request) { | ||
77 | + HttpSession session = request.getSession(false); | ||
78 | + | ||
79 | + if (session == null) { | ||
80 | + return; | ||
81 | + } | ||
82 | + | ||
83 | + session.removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION); | ||
84 | + } | ||
85 | +} |
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.service.security.auth.rest; | ||
17 | + | ||
18 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
19 | +import org.apache.commons.lang3.StringUtils; | ||
20 | +import org.slf4j.Logger; | ||
21 | +import org.slf4j.LoggerFactory; | ||
22 | +import org.springframework.http.HttpMethod; | ||
23 | +import org.springframework.security.authentication.AuthenticationServiceException; | ||
24 | +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | ||
25 | +import org.springframework.security.core.Authentication; | ||
26 | +import org.springframework.security.core.AuthenticationException; | ||
27 | +import org.springframework.security.core.context.SecurityContextHolder; | ||
28 | +import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; | ||
29 | +import org.springframework.security.web.authentication.AuthenticationFailureHandler; | ||
30 | +import org.springframework.security.web.authentication.AuthenticationSuccessHandler; | ||
31 | +import org.thingsboard.server.service.security.exception.AuthMethodNotSupportedException; | ||
32 | + | ||
33 | +import javax.servlet.FilterChain; | ||
34 | +import javax.servlet.ServletException; | ||
35 | +import javax.servlet.http.HttpServletRequest; | ||
36 | +import javax.servlet.http.HttpServletResponse; | ||
37 | +import java.io.IOException; | ||
38 | + | ||
39 | +public class RestLoginProcessingFilter extends AbstractAuthenticationProcessingFilter { | ||
40 | + private static Logger logger = LoggerFactory.getLogger(RestLoginProcessingFilter.class); | ||
41 | + | ||
42 | + private final AuthenticationSuccessHandler successHandler; | ||
43 | + private final AuthenticationFailureHandler failureHandler; | ||
44 | + | ||
45 | + private final ObjectMapper objectMapper; | ||
46 | + | ||
47 | + public RestLoginProcessingFilter(String defaultProcessUrl, AuthenticationSuccessHandler successHandler, | ||
48 | + AuthenticationFailureHandler failureHandler, ObjectMapper mapper) { | ||
49 | + super(defaultProcessUrl); | ||
50 | + this.successHandler = successHandler; | ||
51 | + this.failureHandler = failureHandler; | ||
52 | + this.objectMapper = mapper; | ||
53 | + } | ||
54 | + | ||
55 | + @Override | ||
56 | + public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) | ||
57 | + throws AuthenticationException, IOException, ServletException { | ||
58 | + if (!HttpMethod.POST.name().equals(request.getMethod())) { | ||
59 | + if(logger.isDebugEnabled()) { | ||
60 | + logger.debug("Authentication method not supported. Request method: " + request.getMethod()); | ||
61 | + } | ||
62 | + throw new AuthMethodNotSupportedException("Authentication method not supported"); | ||
63 | + } | ||
64 | + | ||
65 | + LoginRequest loginRequest; | ||
66 | + try { | ||
67 | + loginRequest = objectMapper.readValue(request.getReader(), LoginRequest.class); | ||
68 | + } catch (Exception e) { | ||
69 | + throw new AuthenticationServiceException("Invalid login request payload"); | ||
70 | + } | ||
71 | + | ||
72 | + if (StringUtils.isBlank(loginRequest.getUsername()) || StringUtils.isBlank(loginRequest.getPassword())) { | ||
73 | + throw new AuthenticationServiceException("Username or Password not provided"); | ||
74 | + } | ||
75 | + | ||
76 | + UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword()); | ||
77 | + | ||
78 | + return this.getAuthenticationManager().authenticate(token); | ||
79 | + } | ||
80 | + | ||
81 | + @Override | ||
82 | + protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, | ||
83 | + Authentication authResult) throws IOException, ServletException { | ||
84 | + successHandler.onAuthenticationSuccess(request, response, authResult); | ||
85 | + } | ||
86 | + | ||
87 | + @Override | ||
88 | + protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, | ||
89 | + AuthenticationException failed) throws IOException, ServletException { | ||
90 | + SecurityContextHolder.clearContext(); | ||
91 | + failureHandler.onAuthenticationFailure(request, response, failed); | ||
92 | + } | ||
93 | +} |
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.service.security.device; | ||
17 | + | ||
18 | +import lombok.extern.slf4j.Slf4j; | ||
19 | +import org.springframework.beans.factory.annotation.Autowired; | ||
20 | +import org.springframework.stereotype.Service; | ||
21 | +import org.thingsboard.server.common.data.Device; | ||
22 | +import org.thingsboard.server.common.data.id.DeviceId; | ||
23 | +import org.thingsboard.server.common.data.security.DeviceCredentials; | ||
24 | +import org.thingsboard.server.common.data.security.DeviceCredentialsFilter; | ||
25 | +import org.thingsboard.server.common.transport.auth.DeviceAuthResult; | ||
26 | +import org.thingsboard.server.common.transport.auth.DeviceAuthService; | ||
27 | +import org.thingsboard.server.dao.device.DeviceCredentialsService; | ||
28 | +import org.thingsboard.server.dao.device.DeviceService; | ||
29 | + | ||
30 | +import java.util.Optional; | ||
31 | + | ||
32 | +@Service | ||
33 | +@Slf4j | ||
34 | +public class DefaultDeviceAuthService implements DeviceAuthService { | ||
35 | + | ||
36 | + @Autowired | ||
37 | + DeviceService deviceService; | ||
38 | + | ||
39 | + @Autowired | ||
40 | + DeviceCredentialsService deviceCredentialsService; | ||
41 | + | ||
42 | + @Override | ||
43 | + public DeviceAuthResult process(DeviceCredentialsFilter credentialsFilter) { | ||
44 | + log.trace("Lookup device credentials using filter {}", credentialsFilter); | ||
45 | + DeviceCredentials credentials = deviceCredentialsService.findDeviceCredentialsByCredentialsId(credentialsFilter.getCredentialsId()); | ||
46 | + if (credentials != null) { | ||
47 | + log.trace("Credentials found {}", credentials); | ||
48 | + if (credentials.getCredentialsType() == credentialsFilter.getCredentialsType()) { | ||
49 | + switch (credentials.getCredentialsType()) { | ||
50 | + case ACCESS_TOKEN: | ||
51 | + // Credentials ID matches Credentials value in this | ||
52 | + // primitive case; | ||
53 | + return DeviceAuthResult.of(credentials.getDeviceId()); | ||
54 | + default: | ||
55 | + return DeviceAuthResult.of("Credentials Type is not supported yet!"); | ||
56 | + } | ||
57 | + } else { | ||
58 | + return DeviceAuthResult.of("Credentials Type mismatch!"); | ||
59 | + } | ||
60 | + } else { | ||
61 | + log.trace("Credentials not found!"); | ||
62 | + return DeviceAuthResult.of("Credentials Not Found!"); | ||
63 | + } | ||
64 | + } | ||
65 | + | ||
66 | + @Override | ||
67 | + public Optional<Device> findDeviceById(DeviceId deviceId) { | ||
68 | + return Optional.ofNullable(deviceService.findDeviceById(deviceId)); | ||
69 | + } | ||
70 | +} |
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.service.security.exception; | ||
17 | + | ||
18 | +import org.springframework.security.authentication.AuthenticationServiceException; | ||
19 | + | ||
20 | +public class AuthMethodNotSupportedException extends AuthenticationServiceException { | ||
21 | + private static final long serialVersionUID = 3705043083010304496L; | ||
22 | + | ||
23 | + public AuthMethodNotSupportedException(String msg) { | ||
24 | + super(msg); | ||
25 | + } | ||
26 | +} |
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.service.security.exception; | ||
17 | + | ||
18 | +import org.springframework.security.core.AuthenticationException; | ||
19 | +import org.thingsboard.server.service.security.model.token.JwtToken; | ||
20 | + | ||
21 | +public class JwtExpiredTokenException extends AuthenticationException { | ||
22 | + private static final long serialVersionUID = -5959543783324224864L; | ||
23 | + | ||
24 | + private JwtToken token; | ||
25 | + | ||
26 | + public JwtExpiredTokenException(String msg) { | ||
27 | + super(msg); | ||
28 | + } | ||
29 | + | ||
30 | + public JwtExpiredTokenException(JwtToken token, String msg, Throwable t) { | ||
31 | + super(msg, t); | ||
32 | + this.token = token; | ||
33 | + } | ||
34 | + | ||
35 | + public String token() { | ||
36 | + return this.token.getToken(); | ||
37 | + } | ||
38 | +} |
application/src/main/java/org/thingsboard/server/service/security/model/SecurityUser.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.service.security.model; | ||
17 | + | ||
18 | +import org.springframework.security.core.GrantedAuthority; | ||
19 | +import org.springframework.security.core.authority.SimpleGrantedAuthority; | ||
20 | +import org.thingsboard.server.common.data.User; | ||
21 | +import org.thingsboard.server.common.data.id.UserId; | ||
22 | + | ||
23 | +import java.util.Arrays; | ||
24 | +import java.util.Collection; | ||
25 | +import java.util.stream.Collectors; | ||
26 | + | ||
27 | +public class SecurityUser extends User { | ||
28 | + | ||
29 | + private static final long serialVersionUID = -797397440703066079L; | ||
30 | + | ||
31 | + private Collection<GrantedAuthority> authorities; | ||
32 | + private boolean enabled; | ||
33 | + | ||
34 | + public SecurityUser() { | ||
35 | + super(); | ||
36 | + } | ||
37 | + | ||
38 | + public SecurityUser(UserId id) { | ||
39 | + super(id); | ||
40 | + } | ||
41 | + | ||
42 | + public SecurityUser(User user, boolean enabled) { | ||
43 | + super(user); | ||
44 | + this.enabled = enabled; | ||
45 | + } | ||
46 | + | ||
47 | + public Collection<? extends GrantedAuthority> getAuthorities() { | ||
48 | + if (authorities == null) { | ||
49 | + authorities = Arrays.asList(SecurityUser.this.getAuthority()).stream() | ||
50 | + .map(authority -> new SimpleGrantedAuthority(authority.name())) | ||
51 | + .collect(Collectors.toList()); | ||
52 | + } | ||
53 | + return authorities; | ||
54 | + } | ||
55 | + | ||
56 | + public boolean isEnabled() { | ||
57 | + return enabled; | ||
58 | + } | ||
59 | + | ||
60 | + public void setEnabled(boolean enabled) { | ||
61 | + this.enabled = enabled; | ||
62 | + } | ||
63 | + | ||
64 | +} |
application/src/main/java/org/thingsboard/server/service/security/model/token/AccessJwtToken.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.service.security.model.token; | ||
17 | + | ||
18 | +import com.fasterxml.jackson.annotation.JsonIgnore; | ||
19 | +import io.jsonwebtoken.Claims; | ||
20 | + | ||
21 | +public final class AccessJwtToken implements JwtToken { | ||
22 | + private final String rawToken; | ||
23 | + @JsonIgnore | ||
24 | + private Claims claims; | ||
25 | + | ||
26 | + protected AccessJwtToken(final String token, Claims claims) { | ||
27 | + this.rawToken = token; | ||
28 | + this.claims = claims; | ||
29 | + } | ||
30 | + | ||
31 | + public String getToken() { | ||
32 | + return this.rawToken; | ||
33 | + } | ||
34 | + | ||
35 | + public Claims getClaims() { | ||
36 | + return claims; | ||
37 | + } | ||
38 | +} |
application/src/main/java/org/thingsboard/server/service/security/model/token/JwtToken.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.service.security.model.token; | ||
17 | + | ||
18 | +public interface JwtToken { | ||
19 | + String getToken(); | ||
20 | +} |