From 89d36cc10600c95c139ba037e968e47baadef22e Mon Sep 17 00:00:00 2001 From: Helena Penha Date: Sun, 17 May 2026 20:47:03 -0500 Subject: [PATCH 1/3] feat: add basica authentication --- .../homepage/config/SecurityConfig.java | 57 +++++++++++++++ .../homepage/config/SecurityConfigTest.java | 71 +++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 src/main/java/org/healthiermo/homepage/config/SecurityConfig.java create mode 100644 src/test/java/org/healthiermo/homepage/config/SecurityConfigTest.java diff --git a/src/main/java/org/healthiermo/homepage/config/SecurityConfig.java b/src/main/java/org/healthiermo/homepage/config/SecurityConfig.java new file mode 100644 index 0000000..c44b155 --- /dev/null +++ b/src/main/java/org/healthiermo/homepage/config/SecurityConfig.java @@ -0,0 +1,57 @@ +package org.healthiermo.homepage.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +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.provisioning.InMemoryUserDetailsManager; +import org.springframework.security.web.SecurityFilterChain; + +import static org.springframework.security.config.Customizer.withDefaults; + +@Configuration +@EnableWebSecurity +public class SecurityConfig { + + @Value("${spring.security.user.name}") + private String adminUsername; + + @Value("${spring.security.user.password}") + private String adminPassword ; + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .authorizeHttpRequests(auth -> auth + .requestMatchers("/", "/index", "/*.png", "/*.js", "/tingle-master/**").permitAll() + .anyRequest().authenticated() + ) + .httpBasic(withDefaults()) + .csrf(AbstractHttpConfigurer::disable); + + return http.build(); + } + + @Bean + public UserDetailsService userDetailsService() { + UserDetails user = User.builder() + .username(this.adminUsername) + .password(passwordEncoder().encode(this.adminPassword)) + .roles("USER") + .build(); + + return new InMemoryUserDetailsManager(user); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } +} \ No newline at end of file diff --git a/src/test/java/org/healthiermo/homepage/config/SecurityConfigTest.java b/src/test/java/org/healthiermo/homepage/config/SecurityConfigTest.java new file mode 100644 index 0000000..cb8fb03 --- /dev/null +++ b/src/test/java/org/healthiermo/homepage/config/SecurityConfigTest.java @@ -0,0 +1,71 @@ +package org.healthiermo.homepage.config; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; + +import static org.junit.jupiter.api.Assertions.*; + + +@SpringBootTest +class SecurityConfigTest { + + @Autowired + private SecurityConfig config; + + @Autowired + private PasswordEncoder passwordEncoder; + + @Autowired + private UserDetailsService userDetailsService; + + @DynamicPropertySource + static void overrideProperties(DynamicPropertyRegistry registry) { + registry.add("spring.security.user.name", () -> "admin"); + registry.add("spring.security.user.password", () -> "password"); + } + + @Test + void passwordEncoderShouldNotBeNull() { + assertNotNull(passwordEncoder); + } + + @Test + void passwordEncoderShouldEncodePasswords() { + String rawPassword = "password"; + String encodedPassword = passwordEncoder.encode(rawPassword); + + assertNotNull(encodedPassword); + assertNotEquals(rawPassword, encodedPassword); + assertTrue(passwordEncoder.matches(rawPassword, encodedPassword)); + } + + @Test + void userDetailsServiceShouldNotBeNull() { + assertNotNull(userDetailsService); + } + + @Test + void userDetailsServiceShouldLoadUserByUsername() { + UserDetails user = userDetailsService.loadUserByUsername("admin"); + + assertNotNull(user); + assertEquals("admin", user.getUsername()); + assertTrue(user.getAuthorities().stream() + .anyMatch(a -> a.getAuthority().equals("ROLE_USER"))); + } + + @Test + void userDetailsServiceShouldEncodePassword() { + UserDetails user = userDetailsService.loadUserByUsername("admin"); + + assertNotNull(user.getPassword()); + assertNotEquals("password", user.getPassword()); + assertTrue(passwordEncoder.matches("password", user.getPassword())); + } +} From e19b523b4fe4fed2793b534d5682ccc5f072d1a9 Mon Sep 17 00:00:00 2001 From: Helena Penha Date: Sun, 17 May 2026 20:47:35 -0500 Subject: [PATCH 2/3] feat: add basica authentication --- .env.example | 2 ++ .github/workflows/ci.yml | 2 ++ docker-compose.yml | 2 ++ pom.xml | 4 ++++ src/main/resources/application.yaml | 4 ++++ .../homepage/FileUploadIntegrationTest.java | 10 ++++++++++ 6 files changed, 24 insertions(+) diff --git a/.env.example b/.env.example index f4ff381..033a03e 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,5 @@ SPRING_DATASOURCE_USERNAME=root SPRING_DATASOURCE_PASSWORD=changeme APP_PORT=80 +ADMIN_USER_NAME=admin +ADMIN_USER_PASSWORD=changeme \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c88c7dc..e59f4cf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -66,4 +66,6 @@ jobs: SPRING_DATASOURCE_USERNAME: root SPRING_DATASOURCE_PASSWORD: rootpassword SERVER_PORT: 8080 + ADMIN_USER_NAME: admin + ADMIN_USER_PASSWORD: admin run: ./mvnw test -B diff --git a/docker-compose.yml b/docker-compose.yml index 0fb70f1..7aa3744 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,6 +10,8 @@ services: SPRING_DATASOURCE_URL: jdbc:mysql://db:3306/healthiermo?useSSL=true&useLegacyDatetimeCode=false&serverTimezone=UTC SPRING_DATASOURCE_USERNAME: ${SPRING_DATASOURCE_USERNAME} SPRING_DATASOURCE_PASSWORD: ${SPRING_DATASOURCE_PASSWORD} + ADMIN_USER_NAME: ${ADMIN_USER_NAME} + ADMIN_USER_PASSWORD: ${ADMIN_USER_PASSWORD} depends_on: db: condition: service_healthy diff --git a/pom.xml b/pom.xml index a21bdc9..dd1685a 100644 --- a/pom.xml +++ b/pom.xml @@ -53,6 +53,10 @@ mysql-connector-j runtime + + org.springframework.boot + spring-boot-starter-security + org.springframework.boot spring-boot-starter-webmvc-test diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 0ee201b..efa79a3 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -12,6 +12,10 @@ spring: url: jdbc:mysql://localhost:3306/healthiermo?useSSL=true&useLegacyDatetimeCode=false&serverTimezone=UTC username: ${SPRING_DATASOURCE_USERNAME} password: ${SPRING_DATASOURCE_PASSWORD} + security: + user: + name: ${ADMIN_USER_NAME} + password: ${ADMIN_USER_PASSWORD} server: port: 80 tomcat: diff --git a/src/test/java/org/healthiermo/homepage/FileUploadIntegrationTest.java b/src/test/java/org/healthiermo/homepage/FileUploadIntegrationTest.java index f6b7575..fea185e 100644 --- a/src/test/java/org/healthiermo/homepage/FileUploadIntegrationTest.java +++ b/src/test/java/org/healthiermo/homepage/FileUploadIntegrationTest.java @@ -9,6 +9,7 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.beans.factory.annotation.Value; import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; import org.springframework.util.LinkedMultiValueMap; @@ -32,9 +33,17 @@ class FileUploadIntegrationTest { @LocalServerPort private int port; + @Value("${spring.security.user.name}") + private String username; + + @Value("${spring.security.user.password}") + private String password; + @DynamicPropertySource static void overrideProperties(DynamicPropertyRegistry registry) { registry.add("app.public-data-root", () -> tempDir.toString()); + registry.add("spring.security.user.name", () -> "testuser"); + registry.add("spring.security.user.password", () -> "testpass"); } @Test @@ -57,6 +66,7 @@ public String getFilename() { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.MULTIPART_FORM_DATA); + headers.setBasicAuth(username, password); HttpEntity> request = new HttpEntity<>(body, headers); RestTemplate restTemplate = new RestTemplate(); From 1a286bad7b0a2ad2445ecfb50fc515521e7cd1de Mon Sep 17 00:00:00 2001 From: Helena Penha Date: Tue, 26 May 2026 20:09:44 -0500 Subject: [PATCH 3/3] use pre-hashed password --- .env.example | 2 +- .github/workflows/ci.yml | 2 +- docker-compose.yml | 2 +- .../java/org/healthiermo/homepage/config/SecurityConfig.java | 4 ++-- src/main/resources/application.yaml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.env.example b/.env.example index 033a03e..ec2f963 100644 --- a/.env.example +++ b/.env.example @@ -2,4 +2,4 @@ SPRING_DATASOURCE_USERNAME=root SPRING_DATASOURCE_PASSWORD=changeme APP_PORT=80 ADMIN_USER_NAME=admin -ADMIN_USER_PASSWORD=changeme \ No newline at end of file +ADMIN_USER_HASHED_PASSWORD=changeme \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e59f4cf..783dc82 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,5 +67,5 @@ jobs: SPRING_DATASOURCE_PASSWORD: rootpassword SERVER_PORT: 8080 ADMIN_USER_NAME: admin - ADMIN_USER_PASSWORD: admin + ADMIN_USER_HASHED_PASSWORD: admin run: ./mvnw test -B diff --git a/docker-compose.yml b/docker-compose.yml index 7aa3744..e5ec01a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,7 +11,7 @@ services: SPRING_DATASOURCE_USERNAME: ${SPRING_DATASOURCE_USERNAME} SPRING_DATASOURCE_PASSWORD: ${SPRING_DATASOURCE_PASSWORD} ADMIN_USER_NAME: ${ADMIN_USER_NAME} - ADMIN_USER_PASSWORD: ${ADMIN_USER_PASSWORD} + ADMIN_USER_HASHED_PASSWORD: ${ADMIN_USER_HASHED_PASSWORD} depends_on: db: condition: service_healthy diff --git a/src/main/java/org/healthiermo/homepage/config/SecurityConfig.java b/src/main/java/org/healthiermo/homepage/config/SecurityConfig.java index c44b155..1316ebd 100644 --- a/src/main/java/org/healthiermo/homepage/config/SecurityConfig.java +++ b/src/main/java/org/healthiermo/homepage/config/SecurityConfig.java @@ -40,10 +40,10 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti } @Bean - public UserDetailsService userDetailsService() { + public UserDetailsService x() { UserDetails user = User.builder() .username(this.adminUsername) - .password(passwordEncoder().encode(this.adminPassword)) + .password(this.adminPassword) .roles("USER") .build(); diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index efa79a3..73b4c84 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -15,7 +15,7 @@ spring: security: user: name: ${ADMIN_USER_NAME} - password: ${ADMIN_USER_PASSWORD} + password: ${ADMIN_USER_HASHED_PASSWORD} server: port: 80 tomcat: