#77 made the oas dependency optional and conditional

This commit is contained in:
Fabio Formosa
2022-10-22 00:27:29 +02:00
parent b2906d09f4
commit 7f00f5de99
15 changed files with 179 additions and 94 deletions

View File

@@ -2,6 +2,6 @@ package it.fabioformosa.quartzmanager.api.common.config;
public class OpenAPIConfigConsts {
final static public String BASIC_AUTH_SEC_OAS_SCHEME = "basic-auth";
final static public String QUARTZ_MANAGER_SEC_OAS_SCHEMA = "quartz-manager-auth";
}

View File

@@ -40,19 +40,21 @@
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.springframework.security</groupId>-->
<!-- <artifactId>spring-security-core</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
@@ -116,28 +118,28 @@
</dependency>
<!-- Reactor -->
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-net</artifactId>
<version>2.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>io.projectreactor.spring</groupId>
<artifactId>reactor-spring-context</artifactId>
<version>2.0.7.RELEASE</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>io.projectreactor</groupId>-->
<!-- <artifactId>reactor-core</artifactId>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>io.projectreactor</groupId>-->
<!-- <artifactId>reactor-net</artifactId>-->
<!-- <version>2.0.8.RELEASE</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>io.projectreactor.spring</groupId>-->
<!-- <artifactId>reactor-spring-context</artifactId>-->
<!-- <version>2.0.7.RELEASE</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>io.netty</groupId>-->
<!-- <artifactId>netty-all</artifactId>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-aop</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
@@ -148,16 +150,13 @@
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>${springdoc-openapi.version}</version>
<optional>true</optional>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.springdoc</groupId>-->
<!-- <artifactId>springdoc-openapi-security</artifactId>-->
<!-- <version>${openapi.version}</version>-->
<!-- </dependency>-->
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations</artifactId>
<version>2.1.11</version>
<optional>true</optional>
</dependency>
<!-- TEST -->

View File

@@ -1,68 +1,49 @@
package it.fabioformosa.quartzmanager.api.configuration;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.PathItem;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import io.swagger.v3.oas.models.media.*;
import io.swagger.v3.oas.models.parameters.RequestBody;
import io.swagger.v3.oas.models.responses.ApiResponse;
import io.swagger.v3.oas.models.responses.ApiResponses;
import io.swagger.v3.oas.models.security.SecurityScheme;
import it.fabioformosa.quartzmanager.api.common.config.OpenAPIConfigConsts;
import it.fabioformosa.quartzmanager.api.common.config.QuartzManagerPaths;
import lombok.extern.slf4j.Slf4j;
import org.springdoc.core.GroupedOpenApi;
import org.springdoc.core.customizers.OpenApiCustomiser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Arrays;
import java.util.Optional;
@Slf4j
@Configuration
public class OpenApiConfig {
@ConditionalOnProperty(name = "quartz-manager.oas.enabled")
@ConditionalOnMissingBean
@Bean
public OpenAPI customOpenAPI(@Autowired(required = false) SecurityDiscover securityDiscover) {
OpenAPI openAPI = new OpenAPI()
.info(apiInfo());
if(securityDiscover != null)
openAPI
.components(new Components().addSecuritySchemes(OpenAPIConfigConsts.BASIC_AUTH_SEC_OAS_SCHEME, buildBasicAuthScheme()))
.path(QuartzManagerPaths.QUARTZ_MANAGER_LOGIN_PATH,
new PathItem().post(new Operation()
.operationId("login")
.tags(Arrays.asList("auth"))
.requestBody(new RequestBody().content(
new Content().addMediaType("application/x-www-form-urlencoded", new MediaType().schema(new Schema().type("object")
.addProperties("username", new StringSchema())
.addProperties("password", new PasswordSchema())
.required(Arrays.asList("username", "password"))
))))
.responses(new ApiResponses().addApiResponse("200", new ApiResponse().description("JWT Token to authenticate the next requests")))
.responses(new ApiResponses().addApiResponse("401", new ApiResponse().description("Unauthorized - Username or password are incorrect!")))
));
return openAPI;
}
private SecurityScheme buildBasicAuthScheme() {
return new SecurityScheme()
.type(SecurityScheme.Type.HTTP)
.scheme("bearer")
.bearerFormat("JWT");
}
private Info apiInfo() {
return new Info()
public OpenAPI quartzManagerOpenAPI() {
log.info("No OpenAPI found! Quart Manager is creating it...");
return new OpenAPI().info(new Info()
.title("QUARTZ MANAGER API")
.description("Quartz Manager - REST API")
.version("1.0.0")
.license(new License()
.name("Apache License 2.0")
.url("https://github.com/fabioformosa/quartz-manager/blob/master/LICENSE"));
}
.url("https://github.com/fabioformosa/quartz-manager/blob/master/LICENSE")));
}
@ConditionalOnProperty(name = "quartz-manager.oas.enabled")
@Bean
public GroupedOpenApi quartzManagerStoreOpenApi(@Autowired(required = false) @Qualifier("quartzManagerOpenApiCustomiser") Optional<OpenApiCustomiser> openApiCustomiser) {
String paths[] = {QuartzManagerPaths.QUARTZ_MANAGER_BASE_CONTEXT_PATH + "/**"};
GroupedOpenApi.Builder groupedOpenApiBuilder = GroupedOpenApi.builder().group("quartz-manager").pathsToMatch(paths);
openApiCustomiser.ifPresent(oaCustomizer -> groupedOpenApiBuilder.addOpenApiCustomiser(oaCustomizer));
return groupedOpenApiBuilder.build();
}
}

View File

@@ -17,7 +17,7 @@ import java.util.List;
import java.util.stream.Collectors;
@RequestMapping(QuartzManagerPaths.QUARTZ_MANAGER_BASE_CONTEXT_PATH + "/jobs")
@SecurityRequirement(name = OpenAPIConfigConsts.BASIC_AUTH_SEC_OAS_SCHEME)
@SecurityRequirement(name = OpenAPIConfigConsts.QUARTZ_MANAGER_SEC_OAS_SCHEMA)
@RestController
public class JobController {
final private JobService jobService;

View File

@@ -16,7 +16,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import static it.fabioformosa.quartzmanager.api.common.config.OpenAPIConfigConsts.BASIC_AUTH_SEC_OAS_SCHEME;
import static it.fabioformosa.quartzmanager.api.common.config.OpenAPIConfigConsts.QUARTZ_MANAGER_SEC_OAS_SCHEMA;
import static it.fabioformosa.quartzmanager.api.common.config.QuartzManagerPaths.QUARTZ_MANAGER_BASE_CONTEXT_PATH;
/**
@@ -27,7 +27,7 @@ import static it.fabioformosa.quartzmanager.api.common.config.QuartzManagerPaths
*/
@Slf4j
@RestController
@SecurityRequirement(name = BASIC_AUTH_SEC_OAS_SCHEME)
@SecurityRequirement(name = QUARTZ_MANAGER_SEC_OAS_SCHEMA)
@RequestMapping(SchedulerController.SCHEDULER_CONTROLLER_BASE_URL)
public class SchedulerController {

View File

@@ -23,7 +23,7 @@ import javax.validation.Valid;
@Slf4j
@RequestMapping(SimpleTriggerController.SIMPLE_TRIGGER_CONTROLLER_BASE_URL)
@SecurityRequirement(name = OpenAPIConfigConsts.BASIC_AUTH_SEC_OAS_SCHEME)
@SecurityRequirement(name = OpenAPIConfigConsts.QUARTZ_MANAGER_SEC_OAS_SCHEMA)
@RestController
public class SimpleTriggerController {

View File

@@ -16,12 +16,12 @@ import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import static it.fabioformosa.quartzmanager.api.common.config.OpenAPIConfigConsts.BASIC_AUTH_SEC_OAS_SCHEME;
import static it.fabioformosa.quartzmanager.api.common.config.OpenAPIConfigConsts.QUARTZ_MANAGER_SEC_OAS_SCHEMA;
import static it.fabioformosa.quartzmanager.api.common.config.QuartzManagerPaths.QUARTZ_MANAGER_BASE_CONTEXT_PATH;
@Slf4j
@RequestMapping(TriggerController.TRIGGER_CONTROLLER_BASE_URL)
@SecurityRequirement(name = BASIC_AUTH_SEC_OAS_SCHEME)
@SecurityRequirement(name = QUARTZ_MANAGER_SEC_OAS_SCHEMA)
@RestController
public class TriggerController {

View File

@@ -70,6 +70,7 @@
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>${springdoc-openapi.version}</version>
<optional>true</optional>
</dependency>
<!-- TEST -->

View File

@@ -54,7 +54,7 @@ import static it.fabioformosa.quartzmanager.api.common.config.QuartzManagerPaths
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class QuartzManagerSecurityConfig {
private static final String[] PATTERNS_SWAGGER_UI = {"/swagger-ui.html", "/v3/api-docs/**", "/swagger-resources/**", "/webjars/**"};
private static final String[] PATTERNS_SWAGGER_UI = {"/swagger-ui/**", "/swagger-ui.html", "/v3/api-docs/**", "/swagger-resources/**", "/webjars/**"};
public static final String QUARTZ_MANAGER_API_ANT_MATCHER = QUARTZ_MANAGER_BASE_CONTEXT_PATH + "/**";
public static final String QUARTZ_MANAGER_UI_ANT_MATCHER = QuartzManagerPaths.WEBJAR_PATH + "/**";
@@ -121,11 +121,14 @@ public class QuartzManagerSecurityConfig {
}
@Bean(name = "quartzManagerWebSecurityCustomizer")
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) ->
public WebSecurityCustomizer webSecurityCustomizer(@Value("${quartz-manager.oas.enabled:false}") Boolean oasEnabled) {
return (web) -> {
web.ignoring()//
.antMatchers(HttpMethod.GET, PATTERNS_SWAGGER_UI) //
.antMatchers(HttpMethod.GET, QUARTZ_MANAGER_UI_ANT_MATCHER);
if(BooleanUtils.isNotFalse(oasEnabled))
web.ignoring()
.antMatchers(HttpMethod.GET, PATTERNS_SWAGGER_UI);
};
}
@Bean(name = "quartzManagerCorsConfigurationSource")
@@ -151,8 +154,7 @@ public class QuartzManagerSecurityConfig {
return jwtAuthenticationSuccessHandler;
}
// @Bean
public JwtTokenAuthenticationFilter jwtAuthenticationTokenFilter(UserDetailsService userDetailsService) throws Exception {
public JwtTokenAuthenticationFilter jwtAuthenticationTokenFilter(UserDetailsService userDetailsService) {
return new JwtTokenAuthenticationFilter(jwtTokenHelper(), userDetailsService);
}

View File

@@ -0,0 +1,61 @@
package it.fabioformosa.quartzmanager.api.security.config;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.PathItem;
import io.swagger.v3.oas.models.media.*;
import io.swagger.v3.oas.models.parameters.RequestBody;
import io.swagger.v3.oas.models.responses.ApiResponse;
import io.swagger.v3.oas.models.responses.ApiResponses;
import io.swagger.v3.oas.models.security.SecurityScheme;
import it.fabioformosa.quartzmanager.api.common.config.OpenAPIConfigConsts;
import it.fabioformosa.quartzmanager.api.common.config.QuartzManagerPaths;
import it.fabioformosa.quartzmanager.api.security.properties.JwtSecurityProperties;
import lombok.extern.slf4j.Slf4j;
import org.springdoc.core.customizers.OpenApiCustomiser;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import java.util.Arrays;
@Slf4j
@ConditionalOnProperty(name = "quartz-manager.oas.enabled")
@Configuration
public class SecurityOpenApiConfig {
@Order(Ordered.HIGHEST_PRECEDENCE)
@Bean("quartzManagerOpenApiCustomiser")
public OpenApiCustomiser configureQuartzManagerOpenAPI(JwtSecurityProperties jwtSecurityProps) {
return openAPI -> {
if (jwtSecurityProps.getCookieStrategy().isEnabled() == false)
openAPI
.components(new Components().addSecuritySchemes(OpenAPIConfigConsts.QUARTZ_MANAGER_SEC_OAS_SCHEMA, buildBasicAuthScheme()));
openAPI.path(QuartzManagerPaths.QUARTZ_MANAGER_LOGIN_PATH,
new PathItem().post(new Operation()
.operationId("login")
.tags(Arrays.asList("auth"))
.requestBody(new RequestBody().content(
new Content().addMediaType("application/x-www-form-urlencoded", new MediaType().schema(new Schema().type("object")
.addProperties("username", new StringSchema())
.addProperties("password", new PasswordSchema())
.required(Arrays.asList("username", "password"))
))))
.responses(new ApiResponses().addApiResponse("200", new ApiResponse().description("JWT Token to authenticate the next requests")))
.responses(new ApiResponses().addApiResponse("401", new ApiResponse().description("Unauthorized - Username or password are incorrect!")))
));
};
}
private SecurityScheme buildBasicAuthScheme() {
return new SecurityScheme()
.type(SecurityScheme.Type.HTTP)
.scheme("bearer")
.bearerFormat("JWT")
.description("A JWT Token in required to access this API. You can obtain a JWT Token by providing the username and password in the login API");
}
}

View File

@@ -16,7 +16,7 @@ import static it.fabioformosa.quartzmanager.api.common.config.QuartzManagerPaths
@RestController
@Hidden
@SecurityRequirement(name = OpenAPIConfigConsts.BASIC_AUTH_SEC_OAS_SCHEME)
@SecurityRequirement(name = OpenAPIConfigConsts.QUARTZ_MANAGER_SEC_OAS_SCHEMA)
@RequestMapping(value = QUARTZ_MANAGER_AUTH_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
public class UserController {

View File

@@ -18,7 +18,7 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<springfox.version>2.9.2</springfox.version>
<springdoc-openapi.version>1.5.12</springdoc-openapi.version>
<java.version>9</java.version>
</properties>
@@ -66,6 +66,12 @@
</dependency>
<!-- MISC -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>${springdoc-openapi.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>

View File

@@ -0,0 +1,31 @@
package it.fabioformosa;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import org.springdoc.core.GroupedOpenApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class WebShowcaseOpenApiConfig {
@Bean
public OpenAPI webshowcaseOpenAPI() {
OpenAPI openAPI = new OpenAPI()
.info(new Info()
.title("QUARTZ MANAGER DEMO API")
.description("Quartz Manager- DEMO - REST API")
.version("1.0.0")
.license(new License()
.name("Apache License 2.0")
.url("https://github.com/fabioformosa/quartz-manager/blob/master/LICENSE")));
return openAPI;
}
@Bean
public GroupedOpenApi demoOpenApi() {
return GroupedOpenApi.builder().group("demo").packagesToScan("it.fabioformosa.quartzmanager.controllers").build();
}
}

View File

@@ -2,14 +2,13 @@ package it.fabioformosa.quartzmanager.controllers;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Operation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Hidden
@RestController

View File

@@ -1,5 +1,7 @@
quartz-manager:
jobClassPackages: it.fabioformosa.quartzmanager.jobs
oas:
enabled: true
security:
jwt:
secret: "bibidibobidiboo"
@@ -19,6 +21,9 @@ quartz-manager:
user: "quartzmanager"
password: "quartzmanager"
#springdoc:
# paths-to-exclude: "/quartz-manager/**"
spring:
thymeleaf:
cache: false