Commit d110f5a005ee0410ebee0c72dd2f55517a68597d

Authored by Igor Kulikov
1 parent 944b2f75

Update springfox swagger version.

@@ -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 }
  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>