diff --git a/README.md b/README.md
index 12c3a9e9..957af0ef 100644
--- a/README.md
+++ b/README.md
@@ -125,7 +125,7 @@ docker-compose -p common-api-development -f docker-compose.dev.yml up -d
Building image for production
```bash
cd docker
-DOCKER_BUILDKIT=1 docker build -f Dockerfile.prod -t common-api:4.1.1 ../
+DOCKER_BUILDKIT=1 docker build -f Dockerfile.prod -t common-api:4.1.2 ../
```
docker compose for production
@@ -155,13 +155,15 @@ docker-compose -p common-api -f docker-compose.prod.yml up -d
| SMTP server password | `SMTP_PASSWORD` | secret |
| time for recovery email to expire | `MINUTES_TO_EXPIRE_RECOVERY_CODE` | 20 |
| max requests per minute | `MAX_REQUESTS_PER_MINUTE` | 10 |
+| swagger username | `SWAGGER_USERNAME` | `null` |
+| swagger password | `SWAGGER_PASSWORD` | `null` |
> these variables are defined in: [**application.properties**](./src/main/resources/application.properties)
>
> ```shell
> # to change the value of some environment variable at runtime
> # on execution, just pass it as a parameter. (like --SERVER_PORT=80).
-> $ java -jar api-4.1.1.RELEASE.jar --SERVER_PORT=80
+> $ java -jar api-4.1.2.RELEASE.jar --SERVER_PORT=80
> ```
diff --git a/docker/docker-compose.prod.yml b/docker/docker-compose.prod.yml
index b1276730..f3c5afcf 100644
--- a/docker/docker-compose.prod.yml
+++ b/docker/docker-compose.prod.yml
@@ -17,7 +17,7 @@ services:
- ./.volumes/database:/var/lib/postgresql/data
api:
- image: common-api:4.1.1
+ image: common-api:4.1.2
restart: unless-stopped
container_name: common-api
links:
@@ -30,4 +30,5 @@ services:
DB_PASSWORD: ${DB_PASSWORD}
TOKEN_SECRET: ${TOKEN_SECRET}
DB_SHOW_SQL: "false"
- PRIVATE_SWAGGER: "true"
+ SWAGGER_USERNAME: "example"
+ SWAGGER_PASSWORD: "example"
diff --git a/pom.xml b/pom.xml
index 285bbef7..b6846f5d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -9,7 +9,7 @@
com.github.throyer.common.spring-boot
api
- 4.1.1
+ 4.1.2
CRUD API
Exemplo de api simples com Spring Boot
diff --git a/src/main/java/com/github/throyer/common/springboot/configurations/SpringSecurityConfiguration.java b/src/main/java/com/github/throyer/common/springboot/configurations/SpringSecurityConfiguration.java
index ffc2f0e3..e5f160aa 100644
--- a/src/main/java/com/github/throyer/common/springboot/configurations/SpringSecurityConfiguration.java
+++ b/src/main/java/com/github/throyer/common/springboot/configurations/SpringSecurityConfiguration.java
@@ -2,26 +2,30 @@
import static com.github.throyer.common.springboot.constants.SECURITY.ACESSO_NEGADO_URL;
import static com.github.throyer.common.springboot.constants.SECURITY.DAY_MILLISECONDS;
+import static com.github.throyer.common.springboot.constants.SECURITY.ENCODER;
import static com.github.throyer.common.springboot.constants.SECURITY.HOME_URL;
import static com.github.throyer.common.springboot.constants.SECURITY.LOGIN_ERROR_URL;
import static com.github.throyer.common.springboot.constants.SECURITY.LOGIN_URL;
import static com.github.throyer.common.springboot.constants.SECURITY.LOGOUT_URL;
-import static com.github.throyer.common.springboot.constants.SECURITY.PASSWORD_ENCODER;
import static com.github.throyer.common.springboot.constants.SECURITY.PASSWORD_PARAMETER;
-import static com.github.throyer.common.springboot.constants.SECURITY.PRIVATE_SWAGGER;
-import static com.github.throyer.common.springboot.constants.SECURITY.PUBLIC_API_ROUTES;
+import static com.github.throyer.common.springboot.constants.SECURITY.PUBLICS;
import static com.github.throyer.common.springboot.constants.SECURITY.SESSION_COOKIE_NAME;
import static com.github.throyer.common.springboot.constants.SECURITY.TOKEN_SECRET;
import static com.github.throyer.common.springboot.constants.SECURITY.USERNAME_PARAMETER;
import static com.github.throyer.common.springboot.utils.Responses.forbidden;
+import static java.util.Optional.ofNullable;
import static org.springframework.http.HttpMethod.GET;
import static org.springframework.http.HttpMethod.POST;
import static org.springframework.security.config.http.SessionCreationPolicy.STATELESS;
+import java.util.Optional;
+import java.util.stream.Stream;
+
import com.github.throyer.common.springboot.domain.session.service.SessionService;
import com.github.throyer.common.springboot.middlewares.AuthorizationMiddleware;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
@@ -46,6 +50,9 @@ public class SpringSecurityConfiguration {
private final SessionService sessionService;
private final AuthorizationMiddleware filter;
+ public static String SWAGGER_USERNAME;
+ public static String SWAGGER_PASSWORD;
+
@Autowired
public SpringSecurityConfiguration(
SessionService sessionService,
@@ -57,11 +64,29 @@ public SpringSecurityConfiguration(
@Autowired
protected void globalConfiguration(
- AuthenticationManagerBuilder authentication
+ AuthenticationManagerBuilder authentication,
+ @Value("${swagger.username}") String username,
+ @Value("${swagger.password}") String password
) throws Exception {
+ SpringSecurityConfiguration.SWAGGER_USERNAME = username;
+ SpringSecurityConfiguration.SWAGGER_PASSWORD = password;
+
+ if (Stream
+ .of(ofNullable(SWAGGER_PASSWORD), ofNullable(SWAGGER_USERNAME))
+ .allMatch(Optional::isPresent)) {
+
+ authentication
+ .inMemoryAuthentication()
+ .passwordEncoder(ENCODER)
+ .withUser(username)
+ .password(ENCODER.encode(password))
+ .authorities("SWAGGER");
+ }
+
+
authentication
.userDetailsService(sessionService)
- .passwordEncoder(PASSWORD_ENCODER);
+ .passwordEncoder(ENCODER);
}
@Bean
@@ -74,7 +99,7 @@ public AuthenticationManager authenticationManager(
@Bean
@Order(1)
public SecurityFilterChain api(HttpSecurity http) throws Exception {
- PUBLIC_API_ROUTES.injectOn(http);
+ PUBLICS.injectOn(http);
http
.antMatcher("/api/**")
@@ -137,19 +162,20 @@ public SecurityFilterChain app(HttpSecurity http) throws Exception {
@Bean
@Order(4)
public SecurityFilterChain swagger(HttpSecurity http) throws Exception {
+ if (Stream
+ .of(ofNullable(SWAGGER_PASSWORD), ofNullable(SWAGGER_USERNAME))
+ .allMatch(Optional::isPresent)) {
- if (PRIVATE_SWAGGER) {
http
- .authorizeRequests()
- .antMatchers("/swagger-ui/**", "/swagger-ui.html", "/**.html", "/documentation/**")
+ .antMatcher("/swagger-ui/**")
+ .authorizeRequests()
+ .anyRequest()
.authenticated()
+ .and()
+ .sessionManagement()
+ .sessionCreationPolicy(STATELESS)
.and()
.httpBasic();
- } else {
- http
- .authorizeRequests()
- .antMatchers("/swagger-ui/**", "/swagger-ui.html", "/**.html", "/documentation/**")
- .permitAll();
}
return http.build();
diff --git a/src/main/java/com/github/throyer/common/springboot/constants/SECURITY.java b/src/main/java/com/github/throyer/common/springboot/constants/SECURITY.java
index 93412d44..108c36b7 100644
--- a/src/main/java/com/github/throyer/common/springboot/constants/SECURITY.java
+++ b/src/main/java/com/github/throyer/common/springboot/constants/SECURITY.java
@@ -19,17 +19,15 @@ public SECURITY(
@Value("${token.secret}") String tokenSecret,
@Value("${token.expiration-in-hours}") Integer tokenExpirationInHours,
@Value("${token.refresh.expiration-in-days}") Integer refreshTokenExpirationInDays,
- @Value("${server.servlet.session.cookie.name}") String sessionCookieName,
- @Value("${swagger.is-private}") Boolean privateSwagger
+ @Value("${server.servlet.session.cookie.name}") String sessionCookieName
) {
SECURITY.TOKEN_SECRET = tokenSecret;
SECURITY.TOKEN_EXPIRATION_IN_HOURS = tokenExpirationInHours;
SECURITY.REFRESH_TOKEN_EXPIRATION_IN_DAYS = refreshTokenExpirationInDays;
SECURITY.SESSION_COOKIE_NAME = sessionCookieName;
- SECURITY.PRIVATE_SWAGGER = privateSwagger;
}
- public static final PublicRoutes PUBLIC_API_ROUTES = create()
+ public static final PublicRoutes PUBLICS = create()
.add(GET, "/api")
.add(POST, "/api/users", "/api/sessions/**", "/api/recoveries/**");
@@ -37,7 +35,7 @@ public SECURITY(
public static final JsonWebToken JWT = new JsonWebToken();
public static final Integer PASSWORD_STRENGTH = 10;
- public static final BCryptPasswordEncoder PASSWORD_ENCODER = new BCryptPasswordEncoder(PASSWORD_STRENGTH);
+ public static final BCryptPasswordEncoder ENCODER = new BCryptPasswordEncoder(PASSWORD_STRENGTH);
public static final String ROLES_KEY_ON_JWT = "roles";
@@ -46,7 +44,6 @@ public SECURITY(
public static Integer REFRESH_TOKEN_EXPIRATION_IN_DAYS;
public static String SESSION_COOKIE_NAME;
- public static Boolean PRIVATE_SWAGGER;
public static final String USERNAME_PARAMETER = "email";
public static final String PASSWORD_PARAMETER = "password";
diff --git a/src/main/java/com/github/throyer/common/springboot/domain/session/service/SessionService.java b/src/main/java/com/github/throyer/common/springboot/domain/session/service/SessionService.java
index 647c2e8c..97d41976 100644
--- a/src/main/java/com/github/throyer/common/springboot/domain/session/service/SessionService.java
+++ b/src/main/java/com/github/throyer/common/springboot/domain/session/service/SessionService.java
@@ -43,7 +43,7 @@ public static void authorize(
HttpServletRequest request,
HttpServletResponse response
) {
- if (PUBLIC_API_ROUTES.anyMatch(request)) {
+ if (PUBLICS.anyMatch(request)) {
return;
}
diff --git a/src/main/java/com/github/throyer/common/springboot/domain/user/entity/User.java b/src/main/java/com/github/throyer/common/springboot/domain/user/entity/User.java
index 78deddbf..6101927c 100644
--- a/src/main/java/com/github/throyer/common/springboot/domain/user/entity/User.java
+++ b/src/main/java/com/github/throyer/common/springboot/domain/user/entity/User.java
@@ -21,7 +21,7 @@
import java.util.Objects;
import static com.fasterxml.jackson.annotation.JsonProperty.Access.WRITE_ONLY;
-import static com.github.throyer.common.springboot.constants.SECURITY.PASSWORD_ENCODER;
+import static com.github.throyer.common.springboot.constants.SECURITY.ENCODER;
import static com.github.throyer.common.springboot.domain.management.repository.Queries.NON_DELETED_CLAUSE;
import static com.github.throyer.common.springboot.utils.JSON.stringify;
import static java.util.Objects.hash;
@@ -120,17 +120,16 @@ public void merge(UpdateUserProps props) {
}
public void updatePassword(String newPassword) {
- this.password = PASSWORD_ENCODER
- .encode(newPassword);
+ this.password = ENCODER.encode(newPassword);
}
public Boolean validatePassword(String password) {
- return PASSWORD_ENCODER.matches(password, this.password);
+ return ENCODER.matches(password, this.password);
}
@PrePersist
private void created() {
- this.password = PASSWORD_ENCODER.encode(password);
+ this.password = ENCODER.encode(password);
}
@Override
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 08e17559..381f25d3 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -23,8 +23,6 @@ spring.h2.console.enabled=false
spring.jpa.open-in-view=false
# swagger
-springdoc.swagger-ui.path=/documentation
-springdoc.api-docs.path=/documentation/schemas
springdoc.default-produces-media-type=application/json
springdoc.default-consumes-media-type=application/json
@@ -33,7 +31,9 @@ token.expiration-in-hours=${TOKEN_EXPIRATION_IN_HOURS:24}
token.refresh.expiration-in-days=${REFRESH_TOKEN_EXPIRATION_IN_DAYS:7}
token.secret=${TOKEN_SECRET:secret}
server.servlet.session.cookie.name=API_EXAMPLE_SESSION_ID
-swagger.is-private=${PRIVATE_SWAGGER:true}
+server.servlet.session.cookie.path=/app
+swagger.username=${SWAGGER_USERNAME:#{null}}
+swagger.password=${SWAGGER_PASSWORD:#{null}}
# smtp configurations
spring.mail.host=${SMTP_HOST:smtp.gmail.com}
diff --git a/src/test/java/com/github/throyer/common/springboot/swagger/SwaggerAuthTests.java b/src/test/java/com/github/throyer/common/springboot/swagger/SwaggerAuthTests.java
new file mode 100644
index 00000000..8a3ab0af
--- /dev/null
+++ b/src/test/java/com/github/throyer/common/springboot/swagger/SwaggerAuthTests.java
@@ -0,0 +1,45 @@
+package com.github.throyer.common.springboot.swagger;
+
+import static org.springframework.http.HttpHeaders.AUTHORIZATION;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.web.servlet.MockMvc;
+
+@SpringBootTest(webEnvironment = WebEnvironment.MOCK)
+@AutoConfigureMockMvc
+@TestPropertySource(properties = {
+ "SWAGGER_USERNAME=test",
+ "SWAGGER_PASSWORD=test"})
+public class SwaggerAuthTests {
+
+ @Autowired
+ private MockMvc api;
+
+ @Test
+ @DisplayName("Deve exibir a documentação com basic auth valido")
+ public void should_display_the_swagger_docs_ui_with_valid_credentials() throws Exception {
+
+ var request = get("/swagger-ui/index.html?configUrl=/documentation/schemas/swagger-config")
+ .header(AUTHORIZATION, "Basic dGVzdDp0ZXN0");
+
+ api.perform(request)
+ .andExpect(status().isOk());
+ }
+ @Test
+ @DisplayName("Não deve exibir a documentação com basic auth invalido")
+ public void must_not_display_the_swagger_docs_ui_with_invalid_credentials() throws Exception {
+ var request = get("/swagger-ui/index.html?configUrl=/documentation/schemas/swagger-config")
+ .header(AUTHORIZATION, "Basic anViaWxldTppcmluZXU=");
+
+ api.perform(request)
+ .andExpect(status().isUnauthorized());
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/github/throyer/common/springboot/SwaggerTests.java b/src/test/java/com/github/throyer/common/springboot/swagger/SwaggerTests.java
similarity index 95%
rename from src/test/java/com/github/throyer/common/springboot/SwaggerTests.java
rename to src/test/java/com/github/throyer/common/springboot/swagger/SwaggerTests.java
index c7ff00c1..c1fbca24 100644
--- a/src/test/java/com/github/throyer/common/springboot/SwaggerTests.java
+++ b/src/test/java/com/github/throyer/common/springboot/swagger/SwaggerTests.java
@@ -1,4 +1,4 @@
-package com.github.throyer.common.springboot;
+package com.github.throyer.common.springboot.swagger;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties
index 72e1da1e..8a32687b 100644
--- a/src/test/resources/application.properties
+++ b/src/test/resources/application.properties
@@ -26,7 +26,8 @@ token.expiration-in-hours=24
token.refresh.expiration-in-days=7
token.secret=secret
server.servlet.session.cookie.name=JSESSIONID
-swagger.is-private=false
+swagger.username=${SWAGGER_USERNAME:#{null}}
+swagger.password=${SWAGGER_PASSWORD:#{null}}
# recovery email
recovery.minutes-to-expire=20