diff --git a/clearing-parent/backend-api/pom.xml b/clearing-parent/backend-api/pom.xml index 558019a31..1875a33b2 100644 --- a/clearing-parent/backend-api/pom.xml +++ b/clearing-parent/backend-api/pom.xml @@ -41,6 +41,14 @@ ru.spcex.platform platform-messaging + + io.springfox + springfox-swagger2 + + + io.springfox + springfox-swagger-ui + diff --git a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/config/SwaggerConfig.java b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/config/SwaggerConfig.java new file mode 100644 index 000000000..e6d2929fe --- /dev/null +++ b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/config/SwaggerConfig.java @@ -0,0 +1,39 @@ +package ru.spcex.clearing.backendapi.config; + +import com.google.common.base.Predicates; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +@SuppressWarnings("Guava") +@Configuration +@EnableSwagger2 +public class SwaggerConfig { + + @Bean + public Docket api() { + return new Docket(DocumentationType.SWAGGER_2) + .groupName("clearing-backend-api") + .apiInfo(metadata()) + .select() + .apis(Predicates.not(RequestHandlerSelectors.basePackage("org.springframework.boot"))) + .paths(PathSelectors.any()) + .build() + .useDefaultResponseMessages(false); + } + + private ApiInfo metadata() { + return new ApiInfoBuilder() + .title("Spcex Clearing service") + .description("Сервис клиринга") + .version("0.0.1") + .build(); + } + +} diff --git a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/config/WebConfig.java b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/config/WebConfig.java new file mode 100644 index 000000000..8bd17c36d --- /dev/null +++ b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/config/WebConfig.java @@ -0,0 +1,39 @@ +package ru.spcex.clearing.backendapi.config; + +import org.springframework.boot.web.server.WebServerFactoryCustomizer; +import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.*; + +@SuppressWarnings("Duplicates") +@Configuration +@EnableWebMvc +public class WebConfig implements WebMvcConfigurer { + + @Override + public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { + configurer.enable(); + } + + @Override + public void addViewControllers(ViewControllerRegistry registry) { + registry.addRedirectViewController("/v2/api-docs", "/v2/api-docs?group=api"); + registry.addRedirectViewController("/swagger-resources/configuration/ui", "/swagger-resources/configuration/ui"); + registry.addRedirectViewController("/swagger-resources/configuration/security", "/swagger-resources/configuration/security"); + registry.addRedirectViewController("/swagger-resources", "/swagger-resources"); + registry.addRedirectViewController("", "/swagger-ui.html"); + } + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + registry + .addResourceHandler("/swagger-ui.html**") + .addResourceLocations("classpath:/META-INF/resources/swagger-ui.html"); + } + + @Bean + public WebServerFactoryCustomizer enableDefaultServlet() { + return (factory) -> factory.setRegisterDefaultServlet(true); + } +} diff --git a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/config/element/BackendApiSettings.java b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/config/element/BackendApiSettings.java index cdd40615c..422d18323 100644 --- a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/config/element/BackendApiSettings.java +++ b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/config/element/BackendApiSettings.java @@ -3,6 +3,7 @@ package ru.spcex.clearing.backendapi.config.element; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.PropertySource; import org.springframework.stereotype.Component; +import ru.spcex.clearing.backendapi.security.element.SecuritySettings; import ru.spcex.clearing.platform.messaging.config.element.KafkaProducerSettings; import ru.spcex.platform.imdg.iml.hazelcast.config.HazelcastClientParams; @@ -12,6 +13,7 @@ import ru.spcex.platform.imdg.iml.hazelcast.config.HazelcastClientParams; public class BackendApiSettings { private HazelcastClientParams hazelcast; private KafkaProducerSettings kafka; + private SecuritySettings security; private String exampleSetting; public HazelcastClientParams getHazelcast() { @@ -37,4 +39,12 @@ public class BackendApiSettings { public void setExampleSetting(String exampleSetting) { this.exampleSetting = exampleSetting; } + + public SecuritySettings getSecurity() { + return security; + } + + public void setSecurity(SecuritySettings security) { + this.security = security; + } } diff --git a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/DefaultController.java b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/DefaultController.java index 498e1c646..90a43dcb0 100644 --- a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/DefaultController.java +++ b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/DefaultController.java @@ -1,5 +1,8 @@ package ru.spcex.clearing.backendapi.controller; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; @@ -14,7 +17,10 @@ import ru.spcex.clearing.backendapi.config.element.BackendApiSettings; @Controller public class DefaultController implements InitializingBean { private final Logger log = LoggerFactory.getLogger(getClass()); + private Boolean authorizationDisabled = false; + @ApiOperation(value = "Test backend-api availability.") + @ApiResponses(value = {@ApiResponse(code = 200, message = "OK")}) @RequestMapping(method = RequestMethod.GET, path = "/anonymous/method1", produces = MediaType.TEXT_PLAIN_VALUE) @ResponseBody public String processGet() { @@ -22,19 +28,22 @@ public class DefaultController implements InitializingBean { return "backend-api controller test method"; } + @ApiOperation(value = "Test backend-api availability (for authorized access). Available for anonymous, if authorization-disabled=true.") + @ApiResponses(value = {@ApiResponse(code = 200, message = "OK")}) @RequestMapping(method = RequestMethod.GET, path = "/users/method2", produces = MediaType.TEXT_PLAIN_VALUE) @ResponseBody public String processProtectedGet() { log.info("controller method2"); - return "use post"; + return "backend-api controller test method (authorized endpoint); authorization: " + (authorizationDisabled ? "disabled" : "enabled"); } @Autowired private BackendApiSettings settings; @Override - public void afterPropertiesSet() throws Exception { + public void afterPropertiesSet() { log.info("controller started"); log.info("settings example {}", settings.getExampleSetting()); + this.authorizationDisabled = settings.getSecurity().getAuthorizationDisabled(); } } diff --git a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/cud/CudController.java b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/cud/CudController.java index d5a78360a..6763b14bf 100644 --- a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/cud/CudController.java +++ b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/cud/CudController.java @@ -3,6 +3,7 @@ package ru.spcex.clearing.backendapi.controller.cud; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import io.swagger.annotations.*; import org.apache.kafka.clients.producer.Producer; import org.apache.kafka.clients.producer.ProducerRecord; import org.apache.kafka.clients.producer.RecordMetadata; @@ -30,10 +31,17 @@ public class CudController { this.json = new ObjectMapper(); } + @ApiOperation(value = "create/update/delete business objects. See meta.xml for field descriptions.") + @ApiResponses(value = {@ApiResponse(code = 200, message = "OK", response = MetaDataResponse.class)}) @RequestMapping(value = "/{destination}", method = RequestMethod.POST) @ResponseBody - public BasicSpcexResponse add(@PathVariable("destination") String destination, - @RequestBody String body) throws JsonProcessingException, ExecutionException, InterruptedException { + public MetaDataResponse add( + @ApiParam(value = "Последняя часть URL определяет 'направление', по которому пойдет запрос. " + + "Должно биться с форматом запроса.", required = true, example = "money-market-security-new") + @PathVariable("destination") + String destination, + @ApiParam(value = "Параметры команды в JSON формате, поля см. в meta.xml.", required = true) + @RequestBody String body) throws JsonProcessingException, ExecutionException, InterruptedException { Class> actionClazz = meta.byDestination(destination); if (actionClazz == null) { throw new UnsupportedOperationException("unsupported destination " + destination); @@ -48,11 +56,15 @@ public class CudController { return responseToClient; } + @ApiModel(description="Ответ в результате отправки операции в топик Kafka.") private static class MetaDataResponse extends BasicSpcexResponse { + @ApiModelProperty(value="Offset положенного сообщения") @JsonProperty private Long offset; + @ApiModelProperty(value="Partition положенного сообщения") @JsonProperty private Integer partition; + @ApiModelProperty(value="Название топика, в который было положено сообщение") @JsonProperty private String topic; diff --git a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/response/BasicSpcexResponse.java b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/response/BasicSpcexResponse.java index 78cc506f0..bb3b47597 100644 --- a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/response/BasicSpcexResponse.java +++ b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/response/BasicSpcexResponse.java @@ -1,7 +1,13 @@ package ru.spcex.clearing.backendapi.controller.response; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +@ApiModel(description="Базовый формат ответа") public class BasicSpcexResponse { + @ApiModelProperty(value="Код ответа (успешный 0)", required = true) private int code; + @ApiModelProperty(value="Сообщение ответа") private String message; public int getCode() { diff --git a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/security/WebSecurityConfig.java b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/security/WebSecurityConfig.java index c85304290..da522ad13 100644 --- a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/security/WebSecurityConfig.java +++ b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/security/WebSecurityConfig.java @@ -7,14 +7,22 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper; import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy; import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; +import ru.spcex.clearing.backendapi.config.element.BackendApiSettings; @KeycloakConfiguration @EnableGlobalMethodSecurity(prePostEnabled = true) public class WebSecurityConfig extends KeycloakWebSecurityConfigurerAdapter { + private final Boolean securityDisabled; + + public WebSecurityConfig(BackendApiSettings settings) { + this.securityDisabled = settings.getSecurity().getAuthorizationDisabled(); + } + @Override protected SessionAuthenticationStrategy sessionAuthenticationStrategy() { return new NullAuthenticatedSessionStrategy(); @@ -30,7 +38,7 @@ public class WebSecurityConfig extends KeycloakWebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { super.configure(http); - http + ExpressionUrlAuthorizationConfigurer.AuthorizedUrl anyReq = http // .formLogin() // .loginProcessingUrl("/backend-api-login/perform-login") // .and() @@ -39,8 +47,12 @@ public class WebSecurityConfig extends KeycloakWebSecurityConfigurerAdapter { .antMatchers("/anonymous/**").permitAll() .antMatchers("/sso/login").permitAll() .antMatchers("/error").permitAll() - .antMatchers( "/cud/**").permitAll() //todo remove .antMatchers("/backend-api-login/**").permitAll() - .anyRequest().hasAnyRole("admin", "default-roles-master"); + .anyRequest(); + if (securityDisabled) { + anyReq.permitAll(); + } else { + anyReq.hasAnyRole("admin", "default-roles-master"); + } } } diff --git a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/security/element/SecuritySettings.java b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/security/element/SecuritySettings.java new file mode 100644 index 000000000..d2b48b879 --- /dev/null +++ b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/security/element/SecuritySettings.java @@ -0,0 +1,13 @@ +package ru.spcex.clearing.backendapi.security.element; + +public class SecuritySettings { + private Boolean authorizationDisabled = false; + + public Boolean getAuthorizationDisabled() { + return authorizationDisabled; + } + + public void setAuthorizationDisabled(Boolean authorizationDisabled) { + this.authorizationDisabled = authorizationDisabled; + } +} diff --git a/clearing-parent/backend-api/src/main/resources/application.properties b/clearing-parent/backend-api/src/main/resources/application.properties index 934e3d6b5..721f33f19 100644 --- a/clearing-parent/backend-api/src/main/resources/application.properties +++ b/clearing-parent/backend-api/src/main/resources/application.properties @@ -15,6 +15,8 @@ backend-api.kafka.batch-size=16384 backend-api.kafka.linger-ms=1 backend-api.kafka.buffer-memory=33554432 +backend-api.security.authorization-disabled=true + ##keycloak ##keycloak.auth-server-url=http://10.200.200.147:8080/ diff --git a/pom.xml b/pom.xml index 65d895c46..430ffad39 100644 --- a/pom.xml +++ b/pom.xml @@ -39,6 +39,8 @@ 1.7.33 1.2.10 2.8.1 + 2.9.2 + 1.5.22 @@ -127,6 +129,21 @@ spring-kafka 2.8.1 + + io.springfox + springfox-swagger2 + 2.9.2 + + + io.springfox + springfox-swagger-ui + ${external_libraries.swagger.version} + + + io.swagger + swagger-annotations + ${external_libraries.swagger.annotations.version} +