Showing
13 changed files
with
273 additions
and
45 deletions
@@ -250,8 +250,8 @@ | @@ -250,8 +250,8 @@ | ||
250 | <artifactId>grpc-stub</artifactId> | 250 | <artifactId>grpc-stub</artifactId> |
251 | </dependency> | 251 | </dependency> |
252 | <dependency> | 252 | <dependency> |
253 | - <groupId>io.springfox</groupId> | ||
254 | - <artifactId>springfox-swagger2</artifactId> | 253 | + <groupId>org.thingsboard</groupId> |
254 | + <artifactId>springfox-boot-starter</artifactId> | ||
255 | </dependency> | 255 | </dependency> |
256 | <dependency> | 256 | <dependency> |
257 | <groupId>com.sun.winsw</groupId> | 257 | <groupId>com.sun.winsw</groupId> |
@@ -320,10 +320,6 @@ | @@ -320,10 +320,6 @@ | ||
320 | <artifactId>delight-nashorn-sandbox</artifactId> | 320 | <artifactId>delight-nashorn-sandbox</artifactId> |
321 | </dependency> | 321 | </dependency> |
322 | <dependency> | 322 | <dependency> |
323 | - <groupId>io.springfox.ui</groupId> | ||
324 | - <artifactId>springfox-swagger-ui-rfc6570</artifactId> | ||
325 | - </dependency> | ||
326 | - <dependency> | ||
327 | <groupId>org.passay</groupId> | 323 | <groupId>org.passay</groupId> |
328 | <artifactId>passay</artifactId> | 324 | <artifactId>passay</artifactId> |
329 | </dependency> | 325 | </dependency> |
@@ -20,13 +20,11 @@ import org.springframework.boot.SpringBootConfiguration; | @@ -20,13 +20,11 @@ import org.springframework.boot.SpringBootConfiguration; | ||
20 | import org.springframework.context.annotation.ComponentScan; | 20 | import org.springframework.context.annotation.ComponentScan; |
21 | import org.springframework.scheduling.annotation.EnableAsync; | 21 | import org.springframework.scheduling.annotation.EnableAsync; |
22 | import org.springframework.scheduling.annotation.EnableScheduling; | 22 | import org.springframework.scheduling.annotation.EnableScheduling; |
23 | -import springfox.documentation.swagger2.annotations.EnableSwagger2; | ||
24 | 23 | ||
25 | import java.util.Arrays; | 24 | import java.util.Arrays; |
26 | 25 | ||
27 | @SpringBootConfiguration | 26 | @SpringBootConfiguration |
28 | @EnableAsync | 27 | @EnableAsync |
29 | -@EnableSwagger2 | ||
30 | @EnableScheduling | 28 | @EnableScheduling |
31 | @ComponentScan({"org.thingsboard.server"}) | 29 | @ComponentScan({"org.thingsboard.server"}) |
32 | public class ThingsboardServerApplication { | 30 | public class ThingsboardServerApplication { |
@@ -18,27 +18,60 @@ package org.thingsboard.server.config; | @@ -18,27 +18,60 @@ package org.thingsboard.server.config; | ||
18 | import com.fasterxml.classmate.ResolvedType; | 18 | import com.fasterxml.classmate.ResolvedType; |
19 | import com.fasterxml.classmate.TypeResolver; | 19 | import com.fasterxml.classmate.TypeResolver; |
20 | import com.fasterxml.jackson.databind.JsonNode; | 20 | import com.fasterxml.jackson.databind.JsonNode; |
21 | -import com.google.common.base.Predicate; | 21 | +import org.springframework.beans.factory.annotation.Autowired; |
22 | import org.springframework.beans.factory.annotation.Value; | 22 | import org.springframework.beans.factory.annotation.Value; |
23 | import org.springframework.context.annotation.Bean; | 23 | import org.springframework.context.annotation.Bean; |
24 | import org.springframework.context.annotation.Configuration; | 24 | import org.springframework.context.annotation.Configuration; |
25 | +import org.springframework.core.annotation.Order; | ||
26 | +import org.springframework.http.HttpMethod; | ||
27 | +import org.springframework.http.MediaType; | ||
25 | import org.thingsboard.server.common.data.security.Authority; | 28 | import org.thingsboard.server.common.data.security.Authority; |
29 | +import org.thingsboard.server.exception.ThingsboardErrorResponse; | ||
30 | +import org.thingsboard.server.service.security.auth.rest.LoginRequest; | ||
31 | +import org.thingsboard.server.service.security.auth.rest.LoginResponse; | ||
26 | import springfox.documentation.builders.ApiInfoBuilder; | 32 | import springfox.documentation.builders.ApiInfoBuilder; |
27 | -import springfox.documentation.schema.AlternateTypeRule; | 33 | +import springfox.documentation.builders.OperationBuilder; |
34 | +import springfox.documentation.builders.RepresentationBuilder; | ||
35 | +import springfox.documentation.builders.RequestParameterBuilder; | ||
36 | +import springfox.documentation.builders.ResponseBuilder; | ||
37 | +import springfox.documentation.service.ApiDescription; | ||
28 | import springfox.documentation.service.ApiInfo; | 38 | import springfox.documentation.service.ApiInfo; |
29 | -import springfox.documentation.service.ApiKey; | 39 | +import springfox.documentation.service.ApiListing; |
30 | import springfox.documentation.service.AuthorizationScope; | 40 | import springfox.documentation.service.AuthorizationScope; |
31 | import springfox.documentation.service.Contact; | 41 | import springfox.documentation.service.Contact; |
42 | +import springfox.documentation.service.HttpLoginPasswordScheme; | ||
43 | +import springfox.documentation.service.ParameterType; | ||
44 | +import springfox.documentation.service.Response; | ||
32 | import springfox.documentation.service.SecurityReference; | 45 | import springfox.documentation.service.SecurityReference; |
46 | +import springfox.documentation.service.SecurityScheme; | ||
47 | +import springfox.documentation.service.Tag; | ||
33 | import springfox.documentation.spi.DocumentationType; | 48 | import springfox.documentation.spi.DocumentationType; |
49 | +import springfox.documentation.spi.service.ApiListingBuilderPlugin; | ||
50 | +import springfox.documentation.spi.service.ApiListingScannerPlugin; | ||
51 | +import springfox.documentation.spi.service.contexts.ApiListingContext; | ||
52 | +import springfox.documentation.spi.service.contexts.DocumentationContext; | ||
53 | +import springfox.documentation.spi.service.contexts.OperationContext; | ||
34 | import springfox.documentation.spi.service.contexts.SecurityContext; | 54 | import springfox.documentation.spi.service.contexts.SecurityContext; |
35 | import springfox.documentation.spring.web.plugins.Docket; | 55 | import springfox.documentation.spring.web.plugins.Docket; |
56 | +import springfox.documentation.spring.web.readers.operation.CachingOperationNameGenerator; | ||
57 | +import springfox.documentation.swagger.common.SwaggerPluginSupport; | ||
58 | +import springfox.documentation.swagger.web.DocExpansion; | ||
59 | +import springfox.documentation.swagger.web.ModelRendering; | ||
60 | +import springfox.documentation.swagger.web.OperationsSorter; | ||
61 | +import springfox.documentation.swagger.web.UiConfiguration; | ||
62 | +import springfox.documentation.swagger.web.UiConfigurationBuilder; | ||
36 | 63 | ||
64 | +import java.util.ArrayList; | ||
65 | +import java.util.Collection; | ||
66 | +import java.util.Collections; | ||
37 | import java.util.List; | 67 | import java.util.List; |
68 | +import java.util.Set; | ||
69 | +import java.util.function.Consumer; | ||
70 | +import java.util.function.Predicate; | ||
38 | 71 | ||
39 | -import static com.google.common.base.Predicates.and; | ||
40 | -import static com.google.common.base.Predicates.not; | ||
41 | import static com.google.common.collect.Lists.newArrayList; | 72 | import static com.google.common.collect.Lists.newArrayList; |
73 | +import static java.util.function.Predicate.not; | ||
74 | +import static springfox.documentation.builders.PathSelectors.any; | ||
42 | import static springfox.documentation.builders.PathSelectors.regex; | 75 | import static springfox.documentation.builders.PathSelectors.regex; |
43 | 76 | ||
44 | @Configuration | 77 | @Configuration |
@@ -67,6 +100,9 @@ public class SwaggerConfiguration { | @@ -67,6 +100,9 @@ public class SwaggerConfiguration { | ||
67 | @Value("${swagger.version}") | 100 | @Value("${swagger.version}") |
68 | private String version; | 101 | private String version; |
69 | 102 | ||
103 | + @Autowired | ||
104 | + private CachingOperationNameGenerator operationNames; | ||
105 | + | ||
70 | @Bean | 106 | @Bean |
71 | public Docket thingsboardApi() { | 107 | public Docket thingsboardApi() { |
72 | TypeResolver typeResolver = new TypeResolver(); | 108 | TypeResolver typeResolver = new TypeResolver(); |
@@ -77,29 +113,110 @@ public class SwaggerConfiguration { | @@ -77,29 +113,110 @@ public class SwaggerConfiguration { | ||
77 | typeResolver.resolve( | 113 | typeResolver.resolve( |
78 | String.class); | 114 | String.class); |
79 | 115 | ||
80 | - return new Docket(DocumentationType.SWAGGER_2) | 116 | + return new Docket(DocumentationType.OAS_30) |
81 | .groupName("thingsboard") | 117 | .groupName("thingsboard") |
82 | .apiInfo(apiInfo()) | 118 | .apiInfo(apiInfo()) |
83 | - .alternateTypeRules( | 119 | + .additionalModels( |
120 | + typeResolver.resolve(ThingsboardErrorResponse.class), | ||
121 | + typeResolver.resolve(LoginRequest.class), | ||
122 | + typeResolver.resolve(LoginResponse.class) | ||
123 | + ) | ||
124 | + /* .alternateTypeRules( | ||
84 | new AlternateTypeRule( | 125 | new AlternateTypeRule( |
85 | jsonNodeType, | 126 | jsonNodeType, |
86 | - stringType)) | 127 | + stringType))*/ |
87 | .select() | 128 | .select() |
88 | .paths(apiPaths()) | 129 | .paths(apiPaths()) |
130 | + .paths(any()) | ||
89 | .build() | 131 | .build() |
90 | - .securitySchemes(newArrayList(jwtTokenKey())) | 132 | + .globalResponses(HttpMethod.GET, |
133 | + List.of( | ||
134 | + new ResponseBuilder() | ||
135 | + .code("401") | ||
136 | + .description("Unauthorized") | ||
137 | + .representation(MediaType.APPLICATION_JSON) | ||
138 | + .apply(classRepresentation(ThingsboardErrorResponse.class, true)) | ||
139 | + .build() | ||
140 | + ) | ||
141 | + ) | ||
142 | + .securitySchemes(newArrayList(httpLogin())) | ||
91 | .securityContexts(newArrayList(securityContext())) | 143 | .securityContexts(newArrayList(securityContext())) |
92 | .enableUrlTemplating(true); | 144 | .enableUrlTemplating(true); |
93 | } | 145 | } |
94 | 146 | ||
95 | - private ApiKey jwtTokenKey() { | ||
96 | - return new ApiKey("X-Authorization", "JWT token", "header"); | 147 | + @Bean |
148 | + @Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER) | ||
149 | + ApiListingScannerPlugin loginEndpointListingScanner() { | ||
150 | + return new ApiListingScannerPlugin() { | ||
151 | + @Override | ||
152 | + public List<ApiDescription> apply(DocumentationContext context) { | ||
153 | + return List.of(loginEndpointApiDescription()); | ||
154 | + } | ||
155 | + | ||
156 | + @Override | ||
157 | + public boolean supports(DocumentationType delimiter) { | ||
158 | + return DocumentationType.SWAGGER_2.equals(delimiter) || DocumentationType.OAS_30.equals(delimiter); | ||
159 | + } | ||
160 | + }; | ||
161 | + } | ||
162 | + | ||
163 | + @Bean | ||
164 | + @Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER) | ||
165 | + ApiListingBuilderPlugin loginEndpointListingBuilder() { | ||
166 | + return new ApiListingBuilderPlugin() { | ||
167 | + @Override | ||
168 | + public void apply(ApiListingContext apiListingContext) { | ||
169 | + if (apiListingContext.getResourceGroup().getGroupName().equals("default")) { | ||
170 | + ApiListing apiListing = apiListingContext.apiListingBuilder().build(); | ||
171 | + if (apiListing.getResourcePath().equals("/api/auth/login")) { | ||
172 | + apiListingContext.apiListingBuilder().tags(Set.of(new Tag("login-endpoint", "Login Endpoint"))); | ||
173 | + apiListingContext.apiListingBuilder().description("Login Endpoint"); | ||
174 | + } | ||
175 | + } | ||
176 | + } | ||
177 | + | ||
178 | + @Override | ||
179 | + public boolean supports(DocumentationType delimiter) { | ||
180 | + return DocumentationType.SWAGGER_2.equals(delimiter) || DocumentationType.OAS_30.equals(delimiter); | ||
181 | + } | ||
182 | + }; | ||
183 | + } | ||
184 | + | ||
185 | + @Bean | ||
186 | + UiConfiguration uiConfig() { | ||
187 | + return UiConfigurationBuilder.builder() | ||
188 | + .deepLinking(true) | ||
189 | + .displayOperationId(false) | ||
190 | + .defaultModelsExpandDepth(1) | ||
191 | + .defaultModelExpandDepth(1) | ||
192 | + .defaultModelRendering(ModelRendering.EXAMPLE) | ||
193 | + .displayRequestDuration(false) | ||
194 | + .docExpansion(DocExpansion.NONE) | ||
195 | + .filter(false) | ||
196 | + .maxDisplayedTags(null) | ||
197 | + .operationsSorter(OperationsSorter.ALPHA) | ||
198 | + .showExtensions(false) | ||
199 | + .showCommonExtensions(false) | ||
200 | + .supportedSubmitMethods(UiConfiguration.Constants.DEFAULT_SUBMIT_METHODS) | ||
201 | + .validatorUrl(null) | ||
202 | + .syntaxHighlightActivate(true) | ||
203 | + .syntaxHighlightTheme("agate") | ||
204 | + .build(); | ||
205 | + } | ||
206 | + | ||
207 | + private SecurityScheme httpLogin() { | ||
208 | + return HttpLoginPasswordScheme | ||
209 | + .X_AUTHORIZATION_BUILDER | ||
210 | + .loginEndpoint("/api/auth/login") | ||
211 | + .name("HTTP login form") | ||
212 | + .description("Enter Username / Password") | ||
213 | + .build(); | ||
97 | } | 214 | } |
98 | 215 | ||
99 | private SecurityContext securityContext() { | 216 | private SecurityContext securityContext() { |
100 | return SecurityContext.builder() | 217 | return SecurityContext.builder() |
101 | .securityReferences(defaultAuth()) | 218 | .securityReferences(defaultAuth()) |
102 | - .forPaths(securityPaths()) | 219 | + .operationSelector(securityPathOperationSelector()) |
103 | .build(); | 220 | .build(); |
104 | } | 221 | } |
105 | 222 | ||
@@ -107,11 +224,8 @@ public class SwaggerConfiguration { | @@ -107,11 +224,8 @@ public class SwaggerConfiguration { | ||
107 | return regex(apiPathRegex); | 224 | return regex(apiPathRegex); |
108 | } | 225 | } |
109 | 226 | ||
110 | - private Predicate<String> securityPaths() { | ||
111 | - return and( | ||
112 | - regex(securityPathRegex), | ||
113 | - not(regex(nonSecurityPathRegex)) | ||
114 | - ); | 227 | + private Predicate<OperationContext> securityPathOperationSelector() { |
228 | + return new SecurityPathOperationSelector(securityPathRegex, nonSecurityPathRegex); | ||
115 | } | 229 | } |
116 | 230 | ||
117 | List<SecurityReference> defaultAuth() { | 231 | List<SecurityReference> defaultAuth() { |
@@ -120,7 +234,7 @@ public class SwaggerConfiguration { | @@ -120,7 +234,7 @@ public class SwaggerConfiguration { | ||
120 | authorizationScopes[1] = new AuthorizationScope(Authority.TENANT_ADMIN.name(), "Tenant administrator"); | 234 | authorizationScopes[1] = new AuthorizationScope(Authority.TENANT_ADMIN.name(), "Tenant administrator"); |
121 | authorizationScopes[2] = new AuthorizationScope(Authority.CUSTOMER_USER.name(), "Customer"); | 235 | authorizationScopes[2] = new AuthorizationScope(Authority.CUSTOMER_USER.name(), "Customer"); |
122 | return newArrayList( | 236 | return newArrayList( |
123 | - new SecurityReference("X-Authorization", authorizationScopes)); | 237 | + new SecurityReference("HTTP login form", authorizationScopes)); |
124 | } | 238 | } |
125 | 239 | ||
126 | private ApiInfo apiInfo() { | 240 | private ApiInfo apiInfo() { |
@@ -134,4 +248,75 @@ public class SwaggerConfiguration { | @@ -134,4 +248,75 @@ public class SwaggerConfiguration { | ||
134 | .build(); | 248 | .build(); |
135 | } | 249 | } |
136 | 250 | ||
251 | + private ApiDescription loginEndpointApiDescription() { | ||
252 | + return new ApiDescription(null, "/api/auth/login", "Login method to get user JWT token data", "Login endpoint", Collections.singletonList( | ||
253 | + new OperationBuilder(operationNames) | ||
254 | + .summary("Login method to get user JWT token data") | ||
255 | + .tags(Set.of("login-endpoint")) | ||
256 | + .authorizations(new ArrayList<>()) | ||
257 | + .position(0) | ||
258 | + .codegenMethodNameStem("loginPost") | ||
259 | + .method(HttpMethod.POST) | ||
260 | + .notes("Login method to get user JWT token data.\n\nValue of the response **token** field can be used as JWT token value for authorization.") | ||
261 | + .requestParameters( | ||
262 | + List.of( | ||
263 | + new RequestParameterBuilder() | ||
264 | + .in(ParameterType.BODY) | ||
265 | + .required(true) | ||
266 | + .description("Login request") | ||
267 | + .content(c -> | ||
268 | + c.requestBody(true) | ||
269 | + .representation(MediaType.APPLICATION_JSON) | ||
270 | + .apply(classRepresentation(LoginRequest.class, false)) | ||
271 | + ) | ||
272 | + .build() | ||
273 | + ) | ||
274 | + ) | ||
275 | + .responses(loginResponses()) | ||
276 | + .build() | ||
277 | + ), false); | ||
278 | + } | ||
279 | + | ||
280 | + private Collection<Response> loginResponses() { | ||
281 | + return List.of( | ||
282 | + new ResponseBuilder() | ||
283 | + .code("200") | ||
284 | + .description("OK") | ||
285 | + .representation(MediaType.APPLICATION_JSON) | ||
286 | + .apply(classRepresentation(LoginResponse.class, true)). | ||
287 | + build() | ||
288 | + ); | ||
289 | + } | ||
290 | + | ||
291 | + /** Helper methods **/ | ||
292 | + | ||
293 | + private Consumer<RepresentationBuilder> classRepresentation(Class clazz, boolean isResponse) { | ||
294 | + return r -> r.model( | ||
295 | + m -> | ||
296 | + m.referenceModel(ref -> | ||
297 | + ref.key(k -> | ||
298 | + k.qualifiedModelName(q -> | ||
299 | + q.namespace(clazz.getPackageName()) | ||
300 | + .name(clazz.getSimpleName())).isResponse(isResponse))) | ||
301 | + ); | ||
302 | + } | ||
303 | + | ||
304 | + private static class SecurityPathOperationSelector implements Predicate<OperationContext> { | ||
305 | + | ||
306 | + private final Predicate<String> securityPathSelector; | ||
307 | + | ||
308 | + SecurityPathOperationSelector(String securityPathRegex, String nonSecurityPathRegex) { | ||
309 | + this.securityPathSelector = regex(securityPathRegex).and( | ||
310 | + not( | ||
311 | + regex(nonSecurityPathRegex) | ||
312 | + )); | ||
313 | + } | ||
314 | + | ||
315 | + @Override | ||
316 | + public boolean test(OperationContext operationContext) { | ||
317 | + return this.securityPathSelector.test(operationContext.requestMappingPattern()); | ||
318 | + } | ||
319 | + } | ||
320 | + | ||
321 | + | ||
137 | } | 322 | } |
@@ -64,6 +64,7 @@ import java.util.List; | @@ -64,6 +64,7 @@ import java.util.List; | ||
64 | public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapter { | 64 | public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapter { |
65 | 65 | ||
66 | public static final String JWT_TOKEN_HEADER_PARAM = "X-Authorization"; | 66 | public static final String JWT_TOKEN_HEADER_PARAM = "X-Authorization"; |
67 | + public static final String JWT_TOKEN_HEADER_PARAM_V2 = "Authorization"; | ||
67 | public static final String JWT_TOKEN_QUERY_PARAM = "token"; | 68 | public static final String JWT_TOKEN_QUERY_PARAM = "token"; |
68 | 69 | ||
69 | public static final String WEBJARS_ENTRY_POINT = "/webjars/**"; | 70 | public static final String WEBJARS_ENTRY_POINT = "/webjars/**"; |
@@ -18,12 +18,20 @@ package org.thingsboard.server.config; | @@ -18,12 +18,20 @@ package org.thingsboard.server.config; | ||
18 | import org.springframework.stereotype.Controller; | 18 | import org.springframework.stereotype.Controller; |
19 | import org.springframework.web.bind.annotation.RequestMapping; | 19 | import org.springframework.web.bind.annotation.RequestMapping; |
20 | 20 | ||
21 | +import javax.servlet.http.HttpServletResponse; | ||
22 | +import java.io.IOException; | ||
23 | + | ||
21 | @Controller | 24 | @Controller |
22 | public class WebConfig { | 25 | public class WebConfig { |
23 | 26 | ||
24 | - @RequestMapping(value = {"/assets", "/assets/", "/{path:^(?!api$)(?!assets$)(?!static$)(?!webjars$)[^\\.]*}/**"}) | 27 | + @RequestMapping(value = {"/assets", "/assets/", "/{path:^(?!api$)(?!assets$)(?!static$)(?!webjars$)(?!swagger-ui$)[^\\.]*}/**"}) |
25 | public String redirect() { | 28 | public String redirect() { |
26 | return "forward:/index.html"; | 29 | return "forward:/index.html"; |
27 | } | 30 | } |
28 | 31 | ||
32 | + @RequestMapping("/swagger-ui.html") | ||
33 | + public void redirectSwagger(HttpServletResponse response) throws IOException { | ||
34 | + response.sendRedirect("/swagger-ui/"); | ||
35 | + } | ||
36 | + | ||
29 | } | 37 | } |
@@ -44,6 +44,7 @@ public class ControllerConstants { | @@ -44,6 +44,7 @@ public class ControllerConstants { | ||
44 | protected static final String OTA_PACKAGE_ID_PARAM_DESCRIPTION = "A string value representing the ota package id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'"; | 44 | protected static final String OTA_PACKAGE_ID_PARAM_DESCRIPTION = "A string value representing the ota package id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'"; |
45 | protected static final String ENTITY_TYPE_PARAM_DESCRIPTION = "A string value representing the entity type. For example, 'DEVICE'"; | 45 | protected static final String ENTITY_TYPE_PARAM_DESCRIPTION = "A string value representing the entity type. For example, 'DEVICE'"; |
46 | protected static final String RULE_CHAIN_ID_PARAM_DESCRIPTION = "A string value representing the rule chain id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'"; | 46 | protected static final String RULE_CHAIN_ID_PARAM_DESCRIPTION = "A string value representing the rule chain id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'"; |
47 | + protected static final String RULE_NODE_ID_PARAM_DESCRIPTION = "A string value representing the rule node id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'"; | ||
47 | protected static final String WIDGET_BUNDLE_ID_PARAM_DESCRIPTION = "A string value representing the widget bundle id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'"; | 48 | protected static final String WIDGET_BUNDLE_ID_PARAM_DESCRIPTION = "A string value representing the widget bundle id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'"; |
48 | protected static final String WIDGET_TYPE_ID_PARAM_DESCRIPTION = "A string value representing the widget type id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'"; | 49 | protected static final String WIDGET_TYPE_ID_PARAM_DESCRIPTION = "A string value representing the widget type id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'"; |
49 | protected static final String RESOURCE_ID_PARAM_DESCRIPTION = "A string value representing the resource id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'"; | 50 | protected static final String RESOURCE_ID_PARAM_DESCRIPTION = "A string value representing the resource id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'"; |
@@ -95,6 +95,7 @@ import static org.thingsboard.server.controller.ControllerConstants.RULE_CHAIN_S | @@ -95,6 +95,7 @@ import static org.thingsboard.server.controller.ControllerConstants.RULE_CHAIN_S | ||
95 | import static org.thingsboard.server.controller.ControllerConstants.RULE_CHAIN_TEXT_SEARCH_DESCRIPTION; | 95 | import static org.thingsboard.server.controller.ControllerConstants.RULE_CHAIN_TEXT_SEARCH_DESCRIPTION; |
96 | import static org.thingsboard.server.controller.ControllerConstants.RULE_CHAIN_TYPES_ALLOWABLE_VALUES; | 96 | import static org.thingsboard.server.controller.ControllerConstants.RULE_CHAIN_TYPES_ALLOWABLE_VALUES; |
97 | import static org.thingsboard.server.controller.ControllerConstants.RULE_CHAIN_TYPE_DESCRIPTION; | 97 | import static org.thingsboard.server.controller.ControllerConstants.RULE_CHAIN_TYPE_DESCRIPTION; |
98 | +import static org.thingsboard.server.controller.ControllerConstants.RULE_NODE_ID_PARAM_DESCRIPTION; | ||
98 | import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_ALLOWABLE_VALUES; | 99 | import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_ALLOWABLE_VALUES; |
99 | import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION; | 100 | import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION; |
100 | import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION; | 101 | import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION; |
@@ -434,7 +435,7 @@ public class RuleChainController extends BaseController { | @@ -434,7 +435,7 @@ public class RuleChainController extends BaseController { | ||
434 | @RequestMapping(value = "/ruleNode/{ruleNodeId}/debugIn", method = RequestMethod.GET) | 435 | @RequestMapping(value = "/ruleNode/{ruleNodeId}/debugIn", method = RequestMethod.GET) |
435 | @ResponseBody | 436 | @ResponseBody |
436 | public JsonNode getLatestRuleNodeDebugInput( | 437 | public JsonNode getLatestRuleNodeDebugInput( |
437 | - @ApiParam(value = RULE_CHAIN_ID_PARAM_DESCRIPTION) | 438 | + @ApiParam(value = RULE_NODE_ID_PARAM_DESCRIPTION) |
438 | @PathVariable(RULE_NODE_ID) String strRuleNodeId) throws ThingsboardException { | 439 | @PathVariable(RULE_NODE_ID) String strRuleNodeId) throws ThingsboardException { |
439 | checkParameter(RULE_NODE_ID, strRuleNodeId); | 440 | checkParameter(RULE_NODE_ID, strRuleNodeId); |
440 | try { | 441 | try { |
@@ -79,7 +79,7 @@ public class WidgetsBundleController extends BaseController { | @@ -79,7 +79,7 @@ public class WidgetsBundleController extends BaseController { | ||
79 | 79 | ||
80 | @ApiOperation(value = "Create Or Update Widget Bundle (saveWidgetsBundle)", | 80 | @ApiOperation(value = "Create Or Update Widget Bundle (saveWidgetsBundle)", |
81 | notes = "Create or update the Widget Bundle. " + WIDGET_BUNDLE_DESCRIPTION + " " + | 81 | notes = "Create or update the Widget Bundle. " + WIDGET_BUNDLE_DESCRIPTION + " " + |
82 | - "When creating the bundle, platform generates Widget Bundle Id as [time-based UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_1_(date-time_and_MAC_address). " + | 82 | + "When creating the bundle, platform generates Widget Bundle Id as [time-based UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_1_(date-time_and_MAC_address)). " + |
83 | "The newly created Widget Bundle Id will be present in the response. " + | 83 | "The newly created Widget Bundle Id will be present in the response. " + |
84 | "Specify existing Widget Bundle id to update the Widget Bundle. " + | 84 | "Specify existing Widget Bundle id to update the Widget Bundle. " + |
85 | "Referencing non-existing Widget Bundle Id will cause 'Not Found' error." + | 85 | "Referencing non-existing Widget Bundle Id will cause 'Not Found' error." + |
@@ -30,7 +30,10 @@ public class JwtHeaderTokenExtractor implements TokenExtractor { | @@ -30,7 +30,10 @@ public class JwtHeaderTokenExtractor implements TokenExtractor { | ||
30 | public String extract(HttpServletRequest request) { | 30 | public String extract(HttpServletRequest request) { |
31 | String header = request.getHeader(ThingsboardSecurityConfiguration.JWT_TOKEN_HEADER_PARAM); | 31 | String header = request.getHeader(ThingsboardSecurityConfiguration.JWT_TOKEN_HEADER_PARAM); |
32 | if (StringUtils.isBlank(header)) { | 32 | if (StringUtils.isBlank(header)) { |
33 | - throw new AuthenticationServiceException("Authorization header cannot be blank!"); | 33 | + header = request.getHeader(ThingsboardSecurityConfiguration.JWT_TOKEN_HEADER_PARAM_V2); |
34 | + if (StringUtils.isBlank(header)) { | ||
35 | + throw new AuthenticationServiceException("Authorization header cannot be blank!"); | ||
36 | + } | ||
34 | } | 37 | } |
35 | 38 | ||
36 | if (header.length() < HEADER_PREFIX.length()) { | 39 | if (header.length() < HEADER_PREFIX.length()) { |
@@ -17,9 +17,14 @@ package org.thingsboard.server.service.security.auth.rest; | @@ -17,9 +17,14 @@ package org.thingsboard.server.service.security.auth.rest; | ||
17 | 17 | ||
18 | import com.fasterxml.jackson.annotation.JsonCreator; | 18 | import com.fasterxml.jackson.annotation.JsonCreator; |
19 | import com.fasterxml.jackson.annotation.JsonProperty; | 19 | import com.fasterxml.jackson.annotation.JsonProperty; |
20 | +import io.swagger.annotations.ApiModel; | ||
21 | +import io.swagger.annotations.ApiModelProperty; | ||
20 | 22 | ||
23 | +@ApiModel | ||
21 | public class LoginRequest { | 24 | public class LoginRequest { |
25 | + | ||
22 | private String username; | 26 | private String username; |
27 | + | ||
23 | private String password; | 28 | private String password; |
24 | 29 | ||
25 | @JsonCreator | 30 | @JsonCreator |
@@ -28,10 +33,12 @@ public class LoginRequest { | @@ -28,10 +33,12 @@ public class LoginRequest { | ||
28 | this.password = password; | 33 | this.password = password; |
29 | } | 34 | } |
30 | 35 | ||
36 | + @ApiModelProperty(position = 1, required = true, value = "User email", example = "tenant@thingsboard.org") | ||
31 | public String getUsername() { | 37 | public String getUsername() { |
32 | return username; | 38 | return username; |
33 | } | 39 | } |
34 | 40 | ||
41 | + @ApiModelProperty(position = 2, required = true, value = "User password", example = "tenant") | ||
35 | public String getPassword() { | 42 | public String getPassword() { |
36 | return password; | 43 | return password; |
37 | } | 44 | } |
application/src/main/java/org/thingsboard/server/service/security/auth/rest/LoginResponse.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016-2021 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 io.swagger.annotations.ApiModel; | ||
19 | +import io.swagger.annotations.ApiModelProperty; | ||
20 | +import lombok.Data; | ||
21 | + | ||
22 | +@ApiModel | ||
23 | +@Data | ||
24 | +public class LoginResponse { | ||
25 | + | ||
26 | + @ApiModelProperty(position = 1, required = true, value = "JWT token", | ||
27 | + example = "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ0ZW5hbnRAdGhpbmdzYm9hcmQub3JnIi...") | ||
28 | + private String token; | ||
29 | + | ||
30 | + @ApiModelProperty(position = 2, required = true, value = "Refresh token", | ||
31 | + example = "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ0ZW5hbnRAdGhpbmdzYm9hcmQub3JnIi...") | ||
32 | + private String refreshToken; | ||
33 | + | ||
34 | +} |
@@ -845,9 +845,9 @@ edges: | @@ -845,9 +845,9 @@ edges: | ||
845 | persistToTelemetry: "${EDGES_PERSIST_STATE_TO_TELEMETRY:false}" | 845 | persistToTelemetry: "${EDGES_PERSIST_STATE_TO_TELEMETRY:false}" |
846 | 846 | ||
847 | swagger: | 847 | swagger: |
848 | - api_path_regex: "${SWAGGER_API_PATH_REGEX:/api.*}" | ||
849 | - security_path_regex: "${SWAGGER_SECURITY_PATH_REGEX:/api.*}" | ||
850 | - non_security_path_regex: "${SWAGGER_NON_SECURITY_PATH_REGEX:/api/noauth.*}" | 848 | + api_path_regex: "${SWAGGER_API_PATH_REGEX:/api/.*}" |
849 | + security_path_regex: "${SWAGGER_SECURITY_PATH_REGEX:/api/.*}" | ||
850 | + non_security_path_regex: "${SWAGGER_NON_SECURITY_PATH_REGEX:/api/(?:noauth|v1)/.*}" | ||
851 | title: "${SWAGGER_TITLE:ThingsBoard REST API}" | 851 | title: "${SWAGGER_TITLE:ThingsBoard REST API}" |
852 | description: "${SWAGGER_DESCRIPTION:For instructions how to authorize requests please visit <a href='http://thingsboard.io/docs/reference/rest-api/'>REST API documentation page</a>.}" | 852 | description: "${SWAGGER_DESCRIPTION:For instructions how to authorize requests please visit <a href='http://thingsboard.io/docs/reference/rest-api/'>REST API documentation page</a>.}" |
853 | contact: | 853 | contact: |
@@ -46,8 +46,8 @@ | @@ -46,8 +46,8 @@ | ||
46 | <spring-data-redis.version>2.4.3</spring-data-redis.version> | 46 | <spring-data-redis.version>2.4.3</spring-data-redis.version> |
47 | <jedis.version>3.3.0</jedis.version> | 47 | <jedis.version>3.3.0</jedis.version> |
48 | <jjwt.version>0.7.0</jjwt.version> | 48 | <jjwt.version>0.7.0</jjwt.version> |
49 | - <slf4j.version>1.7.7</slf4j.version> | ||
50 | - <logback.version>1.2.3</logback.version> | 49 | + <slf4j.version>1.7.32</slf4j.version> |
50 | + <logback.version>1.2.6</logback.version> | ||
51 | <rat.version>0.10</rat.version> | 51 | <rat.version>0.10</rat.version> |
52 | <cassandra.version>4.10.0</cassandra.version> | 52 | <cassandra.version>4.10.0</cassandra.version> |
53 | <metrics.version>4.0.5</metrics.version> | 53 | <metrics.version>4.0.5</metrics.version> |
@@ -83,9 +83,8 @@ | @@ -83,9 +83,8 @@ | ||
83 | <rabbitmq.version>4.8.0</rabbitmq.version> | 83 | <rabbitmq.version>4.8.0</rabbitmq.version> |
84 | <surfire.version>2.19.1</surfire.version> | 84 | <surfire.version>2.19.1</surfire.version> |
85 | <jar-plugin.version>3.0.2</jar-plugin.version> | 85 | <jar-plugin.version>3.0.2</jar-plugin.version> |
86 | - <springfox-swagger.version>2.6.1</springfox-swagger.version> | ||
87 | - <springfox-swagger-ui-rfc6570.version>1.0.0</springfox-swagger-ui-rfc6570.version> | ||
88 | - <swagger-annotations.version>1.5.10</swagger-annotations.version> | 86 | + <springfox-swagger.version>3.0.1</springfox-swagger.version> |
87 | + <swagger-annotations.version>1.6.3</swagger-annotations.version> | ||
89 | <spatial4j.version>0.7</spatial4j.version> | 88 | <spatial4j.version>0.7</spatial4j.version> |
90 | <jts.version>1.15.0</jts.version> | 89 | <jts.version>1.15.0</jts.version> |
91 | <bouncycastle.version>1.67</bouncycastle.version> | 90 | <bouncycastle.version>1.67</bouncycastle.version> |
@@ -1620,8 +1619,8 @@ | @@ -1620,8 +1619,8 @@ | ||
1620 | <version>${curator.version}</version> | 1619 | <version>${curator.version}</version> |
1621 | </dependency> | 1620 | </dependency> |
1622 | <dependency> | 1621 | <dependency> |
1623 | - <groupId>io.springfox</groupId> | ||
1624 | - <artifactId>springfox-swagger2</artifactId> | 1622 | + <groupId>org.thingsboard</groupId> |
1623 | + <artifactId>springfox-boot-starter</artifactId> | ||
1625 | <version>${springfox-swagger.version}</version> | 1624 | <version>${springfox-swagger.version}</version> |
1626 | </dependency> | 1625 | </dependency> |
1627 | <dependency> | 1626 | <dependency> |
@@ -1700,11 +1699,6 @@ | @@ -1700,11 +1699,6 @@ | ||
1700 | <version>${fst.version}</version> | 1699 | <version>${fst.version}</version> |
1701 | </dependency> | 1700 | </dependency> |
1702 | <dependency> | 1701 | <dependency> |
1703 | - <groupId>io.springfox.ui</groupId> | ||
1704 | - <artifactId>springfox-swagger-ui-rfc6570</artifactId> | ||
1705 | - <version>${springfox-swagger-ui-rfc6570.version}</version> | ||
1706 | - </dependency> | ||
1707 | - <dependency> | ||
1708 | <groupId>org.locationtech.spatial4j</groupId> | 1702 | <groupId>org.locationtech.spatial4j</groupId> |
1709 | <artifactId>spatial4j</artifactId> | 1703 | <artifactId>spatial4j</artifactId> |
1710 | <version>${spatial4j.version}</version> | 1704 | <version>${spatial4j.version}</version> |