diff --git a/.env.template b/.env.template index a1e3ad5..7c68119 100644 --- a/.env.template +++ b/.env.template @@ -3,6 +3,14 @@ POSTGRES_PASSWORD=your_postgres_password POSTGRES_DB=your_postgres_db POSTGRES_PORT=5432 +# Spring Security +APP_SECURITY_ADMIN_USER=your_admin_username +APP_SECURITY_ADMIN_PASSWORD=your_admin_password +APP_SECURITY_USER=your_api_username +APP_SECURITY_PASSWORD=your_api_password +APP_SECURITY_TEST_USER=your_test_username +APP_SECURITY_TEST_PASSWORD=your_test_password + # Liquibase configuration LB_CHANGELOG=your_changelog_file.yaml LB_OUTPUT_CHANGELOG=your_output_changelog.yaml diff --git a/.github/workflows/sonar.yaml b/.github/workflows/sonar.yaml index 3a3f774..1f29665 100644 --- a/.github/workflows/sonar.yaml +++ b/.github/workflows/sonar.yaml @@ -39,7 +39,8 @@ jobs: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} run: | - mkdir -p src/test/resources - echo ${{ secrets.APPLICATION_TEST_PROPERTIES }} | base64 -d > src/test/resources/application-test.properties + if [ -n "${{ secrets.APPLICATION_TEST_PROPERTIES }}" ]; then + printf '%s' "${{ secrets.APPLICATION_TEST_PROPERTIES }}" | base64 -d >> src/test/resources/application-test.properties + fi echo "spring.sql.init.mode=never" >> src/test/resources/application-test.properties - mvn clean verify sonar:sonar -DskipDocker=true -Dsonar.qualitygate.wait=true -Dit.test="!CucumberIntegrationTest" + mvn clean verify sonar:sonar -DskipDocker=true -Dsonar.qualitygate.wait=true "-Dit.test=!CucumberIntegrationTest" diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 60e2234..13c29ba 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -31,18 +31,18 @@ jobs: - name: Prepare test properties run: | - mkdir -p src/test/resources - echo ${{ secrets.APPLICATION_TEST_PROPERTIES }} | base64 -d > src/test/resources/application-test.properties + if [ -n "${{ secrets.APPLICATION_TEST_PROPERTIES }}" ]; then + printf '%s' "${{ secrets.APPLICATION_TEST_PROPERTIES }}" | base64 -d >> src/test/resources/application-test.properties + fi echo "spring.sql.init.mode=never" >> src/test/resources/application-test.properties - name: Prepare Docker .env for CI tests run: | - cat > .env < .env - name: Install Docker Compose run: | diff --git a/README.md b/README.md index 37f5c89..09356b1 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ A REST API built with Spring Boot that fetches users from [DummyJSON Users API]( --- -## 📦 Prerequisites +## 📦 Prerequisites - Java 25+ - Docker Desktop diff --git a/pom.xml b/pom.xml index 4927ad8..80f07a0 100644 --- a/pom.xml +++ b/pom.xml @@ -69,6 +69,10 @@ org.springframework.boot spring-boot-starter-webmvc + + org.springframework.boot + spring-boot-starter-security + org.springdoc springdoc-openapi-starter-webmvc-ui diff --git a/src/main/java/com/xpeho/spring_boot_java_random_user/config/SecurityConfig.java b/src/main/java/com/xpeho/spring_boot_java_random_user/config/SecurityConfig.java new file mode 100644 index 0000000..ae3d469 --- /dev/null +++ b/src/main/java/com/xpeho/spring_boot_java_random_user/config/SecurityConfig.java @@ -0,0 +1,112 @@ +package com.xpeho.spring_boot_java_random_user.config; + +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.config.Customizer; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.provisioning.InMemoryUserDetailsManager; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.context.NullSecurityContextRepository; + +@Configuration +@EnableWebSecurity +public class SecurityConfig { + + private static final String RANDOM_USERS_PATH = "/random-users/**"; + private static final String RANDOM_USERS_PREFIX = "/random-users"; + private static final String ADMIN_ROLE = "ADMIN"; + + @Value("${app.security.admin.username}") + private String adminUsername; + + @Value("${app.security.admin.password}") + private String adminPassword; + + @Value("${app.security.user.username}") + private String userUsername; + + @Value("${app.security.user.password}") + private String userPassword; + + @Value("${app.security.test.username}") + private String testUsername; + + @Value("${app.security.test.password}") + private String testPassword; + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) { + try { + return http + .csrf(csrf -> csrf.ignoringRequestMatchers(this::isBasicAuthRequest)) + .securityContext(context -> context.securityContextRepository(new NullSecurityContextRepository())) + .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .httpBasic(Customizer.withDefaults()) + .authorizeHttpRequests(auth -> auth + .requestMatchers(getPublicEndpoints()).permitAll() + .requestMatchers(HttpMethod.GET, RANDOM_USERS_PATH).hasAnyRole(ADMIN_ROLE, "USER", "TEST") + .requestMatchers(HttpMethod.POST, RANDOM_USERS_PATH).hasRole(ADMIN_ROLE) + .requestMatchers(HttpMethod.PUT, RANDOM_USERS_PATH).hasRole(ADMIN_ROLE) + .requestMatchers(HttpMethod.DELETE, RANDOM_USERS_PATH).hasRole(ADMIN_ROLE) + .anyRequest().authenticated() + ) + .build(); + } catch (Exception e) { + throw new SecurityConfigurationException("Failed to build Spring Security filter chain", e); + } + } + + + private boolean isBasicAuthRequest(HttpServletRequest request) { + String authHeader = request.getHeader(HttpHeaders.AUTHORIZATION); + String servletPath = request.getServletPath(); + boolean isRandomUsersPath = servletPath != null && servletPath.startsWith(RANDOM_USERS_PREFIX); + return isRandomUsersPath && authHeader != null && authHeader.startsWith("Basic "); + } + + private String[] getPublicEndpoints() { + return new String[]{ + "/api/**", + "/swagger-ui/**", + "/swagger-ui.html", + "/v3/api-docs/**", + "/actuator/health" + }; + } + + @Bean + UserDetailsService userDetailsService(PasswordEncoder passwordEncoder) { + UserDetails admin = User.withUsername(adminUsername) + .password(passwordEncoder.encode(adminPassword)) + .roles(ADMIN_ROLE) + .build(); + + UserDetails user = User.withUsername(userUsername) + .password(passwordEncoder.encode(userPassword)) + .roles("USER") + .build(); + + UserDetails test = User.withUsername(testUsername) + .password(passwordEncoder.encode(testPassword)) + .roles("TEST") + .build(); + + return new InMemoryUserDetailsManager(admin, user, test); + } + + @Bean + PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } +} diff --git a/src/main/java/com/xpeho/spring_boot_java_random_user/config/SecurityConfigurationException.java b/src/main/java/com/xpeho/spring_boot_java_random_user/config/SecurityConfigurationException.java new file mode 100644 index 0000000..470bb5a --- /dev/null +++ b/src/main/java/com/xpeho/spring_boot_java_random_user/config/SecurityConfigurationException.java @@ -0,0 +1,8 @@ +package com.xpeho.spring_boot_java_random_user.config; + +public class SecurityConfigurationException extends RuntimeException { + + public SecurityConfigurationException(String message, Throwable cause) { + super(message, cause); + } +} \ No newline at end of file diff --git a/src/main/java/com/xpeho/spring_boot_java_random_user/presentation/handlers/UserHandler.java b/src/main/java/com/xpeho/spring_boot_java_random_user/presentation/handlers/UserHandler.java index ae2f80e..c6d81c2 100644 --- a/src/main/java/com/xpeho/spring_boot_java_random_user/presentation/handlers/UserHandler.java +++ b/src/main/java/com/xpeho/spring_boot_java_random_user/presentation/handlers/UserHandler.java @@ -27,7 +27,7 @@ public class UserHandler implements UserController { private static final Logger logger = LoggerFactory.getLogger(UserHandler.class); - private static final String USER_NOT_FOUND_LOG = "warning: the requested user does not exist : {}"; + private static final String USER_NOT_FOUND_LOG = "warning: the requested user does not exist: {}"; private final FetchAndSaveRandomUsersUseCase fetchAndSaveRandomUsersUseCase; private final UpdateRandomUserUseCase updateRandomUserUseCase; @@ -119,6 +119,6 @@ public void deleteUserById(int id) { } private void logUserNotFound(UserNotFoundException e) { - logger.warn(USER_NOT_FOUND_LOG, e); + logger.warn(USER_NOT_FOUND_LOG, e.getMessage()); } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 3ec2a53..62f00f1 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,5 +1,13 @@ spring.application.name=spring_boot_java_random_user +# Security +app.security.admin.username=${APP_SECURITY_ADMIN_USER} +app.security.admin.password=${APP_SECURITY_ADMIN_PASSWORD} +app.security.user.username=${APP_SECURITY_USER} +app.security.user.password=${APP_SECURITY_PASSWORD} +app.security.test.username=${APP_SECURITY_TEST_USER} +app.security.test.password=${APP_SECURITY_TEST_PASSWORD} + # Swagger UI custom path springdoc.swagger-ui.path=/api diff --git a/src/test/java/com/xpeho/spring_boot_java_random_user/config/SecurityConfigTest.java b/src/test/java/com/xpeho/spring_boot_java_random_user/config/SecurityConfigTest.java new file mode 100644 index 0000000..bed902f --- /dev/null +++ b/src/test/java/com/xpeho/spring_boot_java_random_user/config/SecurityConfigTest.java @@ -0,0 +1,129 @@ +package com.xpeho.spring_boot_java_random_user.config; + +import jakarta.servlet.http.HttpServletRequest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +class SecurityConfigTest { + + private final SecurityConfig securityConfig = new SecurityConfig(); + + @BeforeEach + void setUp() { + ReflectionTestUtils.setField(securityConfig, "adminUsername", "admin"); + ReflectionTestUtils.setField(securityConfig, "adminPassword", "admin123"); + ReflectionTestUtils.setField(securityConfig, "userUsername", "apiuser"); + ReflectionTestUtils.setField(securityConfig, "userPassword", "changeit"); + ReflectionTestUtils.setField(securityConfig, "testUsername", "testuser"); + ReflectionTestUtils.setField(securityConfig, "testPassword", "testpass"); + } + + @Test + void shouldEncodePasswordsWithBcrypt() { + PasswordEncoder passwordEncoder = securityConfig.passwordEncoder(); + + assertThat(passwordEncoder).isInstanceOf(BCryptPasswordEncoder.class); + assertThat(passwordEncoder.matches("admin123", passwordEncoder.encode("admin123"))).isTrue(); + } + + @Test + void shouldCreateInMemoryUsersWithExpectedRoles() { + PasswordEncoder passwordEncoder = securityConfig.passwordEncoder(); + + UserDetailsService userDetailsService = securityConfig.userDetailsService(passwordEncoder); + + UserDetails admin = userDetailsService.loadUserByUsername("admin"); + UserDetails user = userDetailsService.loadUserByUsername("apiuser"); + UserDetails test = userDetailsService.loadUserByUsername("testuser"); + + assertThat(admin.getAuthorities()).extracting("authority").containsExactly("ROLE_ADMIN"); + assertThat(user.getAuthorities()).extracting("authority").containsExactly("ROLE_USER"); + assertThat(test.getAuthorities()).extracting("authority").containsExactly("ROLE_TEST"); + } + + @Test + void shouldRecognizeBasicAuthRequestsOnRandomUsersPath() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setServletPath("/random-users/123"); + request.addHeader("Authorization", "Basic dGVzdDp0ZXN0"); + + boolean result = ReflectionTestUtils.invokeMethod(securityConfig, "isBasicAuthRequest", request); + + assertThat(result).isTrue(); + } + + @Test + void shouldRejectNonBasicAuthOrNonRandomUsersRequests() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setServletPath("/health"); + request.addHeader("Authorization", "Bearer token"); + + boolean result = ReflectionTestUtils.invokeMethod(securityConfig, "isBasicAuthRequest", request); + + assertThat(result).isFalse(); + } + + @Test + void shouldRejectRandomUsersRequestWithoutAuthHeader() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setServletPath("/random-users/123"); + + boolean result = ReflectionTestUtils.invokeMethod(securityConfig, "isBasicAuthRequest", request); + + assertThat(result).isFalse(); + } + + @Test + void shouldRejectRandomUsersRequestWithNonBasicAuthHeader() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setServletPath("/random-users/123"); + request.addHeader("Authorization", "Bearer token"); + + boolean result = ReflectionTestUtils.invokeMethod(securityConfig, "isBasicAuthRequest", request); + + assertThat(result).isFalse(); + } + + @Test + void shouldRejectWhenServletPathIsNull() { + HttpServletRequest request = mock(HttpServletRequest.class); + when(request.getServletPath()).thenReturn(null); + when(request.getHeader("Authorization")).thenReturn("Basic dGVzdDp0ZXN0"); + + boolean result = ReflectionTestUtils.invokeMethod(securityConfig, "isBasicAuthRequest", request); + + assertThat(result).isFalse(); + } + + @Test + void shouldExposePublicEndpoints() { + String[] endpoints = ReflectionTestUtils.invokeMethod(securityConfig, "getPublicEndpoints"); + + assertThat(endpoints).contains( + "/api/**", + "/swagger-ui/**", + "/swagger-ui.html", + "/v3/api-docs/**", + "/actuator/health" + ); + } + + @Test + void shouldWrapFilterChainConfigurationException() { + assertThatThrownBy(() -> securityConfig.securityFilterChain(null)) + .isInstanceOf(SecurityConfigurationException.class) + .hasMessage("Failed to build Spring Security filter chain") + .hasCauseInstanceOf(NullPointerException.class); + } +} diff --git a/src/test/java/com/xpeho/spring_boot_java_random_user/config/SecurityConfigurationExceptionTest.java b/src/test/java/com/xpeho/spring_boot_java_random_user/config/SecurityConfigurationExceptionTest.java new file mode 100644 index 0000000..4c96579 --- /dev/null +++ b/src/test/java/com/xpeho/spring_boot_java_random_user/config/SecurityConfigurationExceptionTest.java @@ -0,0 +1,18 @@ +package com.xpeho.spring_boot_java_random_user.config; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class SecurityConfigurationExceptionTest { + + @Test + void shouldExposeMessageAndCause() { + IllegalStateException cause = new IllegalStateException("boom"); + SecurityConfigurationException exception = new SecurityConfigurationException("Failed to build Spring Security filter chain", cause); + + assertThat(exception) + .hasMessage("Failed to build Spring Security filter chain") + .hasCause(cause); + } +} \ No newline at end of file diff --git a/src/test/java/com/xpeho/spring_boot_java_random_user/presentation/UserGetByIdContainerTest.java b/src/test/java/com/xpeho/spring_boot_java_random_user/presentation/UserGetByIdContainerTest.java index 85d78bf..1468bd9 100644 --- a/src/test/java/com/xpeho/spring_boot_java_random_user/presentation/UserGetByIdContainerTest.java +++ b/src/test/java/com/xpeho/spring_boot_java_random_user/presentation/UserGetByIdContainerTest.java @@ -26,11 +26,22 @@ webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = { "spring.sql.init.mode=never", - "spring.jpa.hibernate.ddl-auto=create-drop" + "spring.jpa.hibernate.ddl-auto=create-drop", + "app.security.admin.username=testadmin", + "app.security.admin.password=testadminpass", + "app.security.user.username=testuser", + "app.security.user.password=testpass", + "app.security.test.username=testviewer", + "app.security.test.password=testviewerpass", + "logging.level.com.xpeho.spring_boot_java_random_user.presentation.handlers=OFF", + "logging.level.com.zaxxer.hikari.pool.PoolBase=ERROR" } ) class UserGetByIdContainerTest { + private static final String TEST_USERNAME = "testuser"; + private static final String TEST_PASSWORD = "testpass"; + @Container @ServiceConnection static PostgreSQLContainer postgres = new PostgreSQLContainer("postgres:17-alpine"); @@ -61,8 +72,8 @@ void shouldReturnUserByIdWhenUserExists() { User saved = userRepository.saveAndFlush(user); - ResponseEntity response = restTemplate.getForEntity( - "/random-users/{id}", + ResponseEntity response = restTemplate.withBasicAuth(TEST_USERNAME, TEST_PASSWORD).getForEntity( + "/random-users/{id}", UserEntity.class, saved.getId() ); @@ -77,10 +88,10 @@ void shouldReturnUserByIdWhenUserExists() { @Test @DisplayName("GET /random-users/{id} should return 404 when user does not exist") void shouldReturnNotFoundWhenUserDoesNotExist() { - ResponseEntity response = restTemplate.getForEntity( - "/random-users/{id}", + ResponseEntity response = restTemplate.withBasicAuth(TEST_USERNAME, TEST_PASSWORD).getForEntity( + "/random-users/{id}", UserEntity.class, - -1 + -1 ); assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); diff --git a/src/test/java/com/xpeho/spring_boot_java_random_user/presentation/exceptions/GlobalExceptionHandlerTest.java b/src/test/java/com/xpeho/spring_boot_java_random_user/presentation/exceptions/GlobalExceptionHandlerTest.java index 2c27fa2..f916a73 100644 --- a/src/test/java/com/xpeho/spring_boot_java_random_user/presentation/exceptions/GlobalExceptionHandlerTest.java +++ b/src/test/java/com/xpeho/spring_boot_java_random_user/presentation/exceptions/GlobalExceptionHandlerTest.java @@ -79,9 +79,9 @@ void shouldReturnNotFoundWhenUserNotFoundException() { } @Test - @DisplayName("Should return 500 INTERNAL_SERVER_ERROR for generic exceptions") - void shouldReturnInternalServerErrorForGenericException() { - Exception ex = new Exception("Something went wrong"); + @DisplayName("Should return 500 INTERNAL_SERVER_ERROR for runtime exceptions") + void shouldReturnInternalServerErrorForRuntimeException() { + RuntimeException ex = new RuntimeException("Something went wrong"); ResponseEntity response = handler.handleGenericException(ex); assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode()); diff --git a/src/test/java/feature/SpringIntegrationTest.java b/src/test/java/feature/SpringIntegrationTest.java index ecfe69b..a3d8d66 100644 --- a/src/test/java/feature/SpringIntegrationTest.java +++ b/src/test/java/feature/SpringIntegrationTest.java @@ -7,6 +7,7 @@ import org.springframework.boot.resttestclient.autoconfigure.AutoConfigureTestRestTemplate; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; @@ -27,6 +28,12 @@ ) public class SpringIntegrationTest { + @Value("${app.security.admin.username}") + private String testUsername; + + @Value("${app.security.admin.password}") + private String testPassword; + @Autowired protected TestRestTemplate restTemplate; @@ -37,7 +44,9 @@ public class SpringIntegrationTest { protected void executeGet(String path) { String url = "http://localhost:" + port + path; - latestResponse = restTemplate.getForEntity(url, String.class); + latestResponse = restTemplate + .withBasicAuth(testUsername, testPassword) + .getForEntity(url, String.class); } protected void executePost(String path, Object payload) { @@ -45,19 +54,25 @@ protected void executePost(String path, Object payload) { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); HttpEntity request = new HttpEntity<>(payload, headers); - latestResponse = restTemplate.postForEntity(url, request, String.class); + latestResponse = restTemplate + .withBasicAuth(testUsername, testPassword) + .postForEntity(url, request, String.class); } protected void executeDelete(String path) { String url = "http://localhost:" + port + path; - latestResponse = restTemplate.exchange(url, HttpMethod.DELETE, HttpEntity.EMPTY, String.class); + latestResponse = restTemplate + .withBasicAuth(testUsername, testPassword) + .exchange(url, HttpMethod.DELETE, HttpEntity.EMPTY, String.class); } - protected void executePut(String path, Object payload) { - String url = "http://localhost:" + port + path; - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - HttpEntity request = new HttpEntity<>(payload, headers); - latestResponse = restTemplate.exchange(url, HttpMethod.PUT, request, String.class); - } + protected void executePut(String path, Object payload) { + String url = "http://localhost:" + port + path; + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity request = new HttpEntity<>(payload, headers); + latestResponse = restTemplate + .withBasicAuth(testUsername, testPassword) + .exchange(url, HttpMethod.PUT, request, String.class); + } } diff --git a/src/test/resources/application-test.properties b/src/test/resources/application-test.properties index 0f7b4dc..45e847f 100644 --- a/src/test/resources/application-test.properties +++ b/src/test/resources/application-test.properties @@ -1,6 +1,14 @@ -spring.datasource.url=${H2_URL};MODE=PostgreSQL;DATABASE_TO_UPPER=false;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE +spring.datasource.url=${H2_URL:jdbc:h2:mem:mydb};MODE=PostgreSQL;DATABASE_TO_UPPER=false;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE spring.datasource.driver-class-name=org.h2.Driver -spring.datasource.username=${H2_USERNAME} -spring.datasource.password=${H2_PASSWORD} +spring.datasource.username=${H2_USERNAME:myusername} +spring.datasource.password=${H2_PASSWORD:mypassword} +app.security.admin.username=testadmin +app.security.admin.password=testadminpass +app.security.user.username=testuser +app.security.user.password=testpass +app.security.test.username=testviewer +app.security.test.password=testviewerpass spring.sql.init.mode=never spring.docker.compose.enabled=false +logging.level.com.xpeho.spring_boot_java_random_user.presentation.handlers=OFF +logging.level.com.zaxxer.hikari.pool.PoolBase=ERROR diff --git a/src/test/resources/application-test.properties.template b/src/test/resources/application-test.properties.template index bd145a7..b9bd7d4 100644 --- a/src/test/resources/application-test.properties.template +++ b/src/test/resources/application-test.properties.template @@ -4,3 +4,5 @@ spring.datasource.username=myusername spring.datasource.password=mypassword spring.sql.init.mode=never spring.docker.compose.enabled=false +logging.level.com.xpeho.spring_boot_java_random_user.presentation.handlers=OFF +logging.level.com.zaxxer.hikari.pool.PoolBase=ERROR