Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
d72b5b4
Update project to Java 21, Spring Boot 4.0.2, and add new test depend…
mark79-github Feb 13, 2026
f13da7b
Update system properties to require Java 21
mark79-github Feb 13, 2026
919e5fd
Update Dockerfile to use Java 21 alpine image
mark79-github Feb 13, 2026
ceeb76b
Restrict /log/** to ROOT role and update static resource matchers
mark79-github Feb 13, 2026
0dc55df
Fix import for PathRequest to match Spring Boot 4 changes
mark79-github Feb 13, 2026
bfd3490
Refactor HomeController to improve authentication check using Authent…
mark79-github Feb 13, 2026
07b968b
Replace ROLE_ROOT with ROOT in PreAuthorize annotations for consistency
mark79-github Feb 13, 2026
617c01e
Remove unnecessary @Async and @EnableAsync annotations from ScheduleS…
mark79-github Feb 13, 2026
e7e3ef4
Fix incorrect import to use correct DataJpaTest annotation
mark79-github Feb 14, 2026
b15a55a
Fix incorrect import in UserRepositoryTest
mark79-github Feb 14, 2026
dd63190
Fix incorrect import to use correct DataJpaTest annotation
mark79-github Feb 14, 2026
a3e2d29
Fix incorrect import in LogRepositoryTest
mark79-github Feb 14, 2026
d3bbe1d
Fix incorrect import to use correct DataJpaTest annotation
mark79-github Feb 15, 2026
35d3b1a
Fix incorrect import in InvoiceRepositoryTest
mark79-github Feb 15, 2026
2f1f7c2
Fix incorrect annotation in CompanyControllerTest and replace WithMoc…
mark79-github Feb 15, 2026
7db1700
Fix incorrect imports and assertions
mark79-github Feb 15, 2026
73e3f8a
Remove redundant exception declaration from configureSecurity method
mark79-github Feb 16, 2026
df74baa
Fix assertion method in InvoiceRepositoryTest
mark79-github Feb 16, 2026
a40b3a1
Fix incorrect imports and replace WithMockUser with MockMvc user setup
mark79-github Feb 16, 2026
f1923cf
Fix incorrect imports and adjust LogControllerTest to replace WithMoc…
mark79-github Feb 16, 2026
a1bd77d
Fix incorrect imports in HomeControllerTest and replace WithMockUser …
mark79-github Feb 17, 2026
e7d65d7
Update GitHub Actions workflow to use JDK 21
mark79-github Feb 17, 2026
9b67d4b
Update dependency versions in pom.xml
mark79-github Feb 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up JDK 17
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: 17
java-version: 21
distribution: 'zulu'
- name: Cache SonarQube packages
uses: actions/cache@v4
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM eclipse-temurin:17-jdk-alpine
FROM eclipse-temurin:21-jdk-alpine

WORKDIR /app

Expand Down
26 changes: 18 additions & 8 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,21 @@
<version>1.0.0</version>
<packaging>war</packaging>
<name>Simple-Invoicing</name>
<description>Final Project Spring Boot Advanced May 2024</description>
<description>Final Project Spring Boot Advanced May 2024</description>

<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>17</java.version>
<spring-boot.version>3.5.7</spring-boot.version>
<java.version>21</java.version>
<spring-boot.version>4.0.2</spring-boot.version>
<modelmapper.version>3.2.6</modelmapper.version>
<cloudinary-http5.version>2.3.2</cloudinary-http5.version>
<mysql-connector-j.version>9.5.0</mysql-connector-j.version>
<postgresql.version>42.7.8</postgresql.version>
<mysql-connector-j.version>9.6.0</mysql-connector-j.version>
<postgresql.version>42.7.10</postgresql.version>
<commons-lang3.version>3.20.0</commons-lang3.version>
<jacoco.version>0.8.14</jacoco.version>
<maven-compiler-plugin.version>3.14.1</maven-compiler-plugin.version>
<maven-compiler-plugin.version>3.15.0</maven-compiler-plugin.version>
<lombok.version>1.18.42</lombok.version>
<sonar.organization>mark79-github</sonar.organization>
</properties>
Expand Down Expand Up @@ -91,6 +91,16 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package bg.softuni.invoice.config;

import lombok.AllArgsConstructor;
import org.springframework.boot.autoconfigure.security.servlet.PathRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
Expand All @@ -28,15 +27,22 @@ public class SecurityConfiguration {
private final BCryptPasswordEncoder bCryptPasswordEncoder;

@Bean
public SecurityFilterChain configureSecurity(HttpSecurity httpSecurity) throws Exception {
public SecurityFilterChain configureSecurity(HttpSecurity httpSecurity) {
httpSecurity
.cors(AbstractHttpConfigurer::disable)
.csrf(csrf -> csrf.csrfTokenRepository(csrfTokenRepository()))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/").permitAll()
.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
.requestMatchers(
"/css/**",
"/js/**",
"/images/**",
"/webjars/**",
"/favicon.ico"
).permitAll()
.requestMatchers("/actuator/health").permitAll()
.requestMatchers("/user/register", "/user/login").anonymous()
.requestMatchers("/log/**").hasRole("ROOT")
.anyRequest().authenticated()
)
.formLogin(form -> form
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
package bg.softuni.invoice.service;

import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;

@EnableAsync
public interface ScheduleService {

@Async
void deleteLogs();

@Async
void changeStatus();
}

Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,23 @@

import bg.softuni.invoice.web.annotation.PageTitle;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

import java.security.Principal;

@Controller
public class HomeController {

@GetMapping("/")
@PageTitle("invoices")
public String index(Principal principal) {
public String index(Authentication authentication) {

boolean isLoggedIn = authentication != null
&& authentication.isAuthenticated()
&& !(authentication instanceof AnonymousAuthenticationToken);

if (principal != null) {
if (isLoggedIn) {
return "home/home";
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ public String profileConfirm(@Valid

@GetMapping("/all")
@PageTitle("User all")
@PreAuthorize("hasRole('ROLE_ROOT')")
@PreAuthorize("hasRole('ROOT')")
public String allUsers(Model model) {

List<UserViewModel> users = this.userService.getAllUsers()
Expand All @@ -172,31 +172,31 @@ public String allUsers(Model model) {
}

@PostMapping("/set-admin/{id}")
@PreAuthorize("hasRole('ROLE_ROOT')")
@PreAuthorize("hasRole('ROOT')")
public String setAdminRole(@PathVariable String id) {
this.userService.setAdmin(id);

return REDIRECT_USER_ALL;
}

@PostMapping("/set-user/{id}")
@PreAuthorize("hasRole('ROLE_ROOT')")
@PreAuthorize("hasRole('ROOT')")
public String setUserRole(@PathVariable String id) {
this.userService.setUser(id);

return REDIRECT_USER_ALL;
}

@PostMapping("/set-enabled/{id}")
@PreAuthorize("hasRole('ROLE_ROOT')")
@PreAuthorize("hasRole('ROOT')")
public String setEnabled(@PathVariable String id) {
this.userService.setUserEnabled(id);

return REDIRECT_USER_ALL;
}

@PostMapping("/set-disabled/{id}")
@PreAuthorize("hasRole('ROLE_ROOT')")
@PreAuthorize("hasRole('ROOT')")
public String setDisabled(@PathVariable String id) {
this.userService.setUserDisabled(id);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.security.servlet.PathRequest;
import org.springframework.boot.security.autoconfigure.web.servlet.PathRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import org.junit.jupiter.params.provider.NullSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.data.jpa.test.autoconfigure.DataJpaTest;

import java.util.List;
import java.util.Optional;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.data.jpa.test.autoconfigure.DataJpaTest;

import java.math.BigDecimal;
import java.time.LocalDate;
Expand Down Expand Up @@ -84,7 +84,7 @@ void testGetAllByUser_withDifferentUser_returnsOnlyRelevantInvoices() {
List<Invoice> resultForUser2 = invoiceRepository.getAllByUser(user2);

assertThat(resultForUser1).hasSize(1);
assertThat(resultForUser1.get(0).getTotalValue()).isEqualByComparingTo("200.00");
assertThat(resultForUser1.getFirst().getTotalValue()).isEqualByComparingTo("200.00");
assertThat(resultForUser2).isEmpty();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import jakarta.persistence.EntityManager;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.data.jpa.test.autoconfigure.DataJpaTest;

import java.math.BigDecimal;
import java.util.Optional;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import jakarta.persistence.EntityManager;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.data.jpa.test.autoconfigure.DataJpaTest;

import java.time.LocalDateTime;
import java.util.List;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import jakarta.persistence.EntityManager;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.data.jpa.test.autoconfigure.DataJpaTest;

import java.util.Optional;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import jakarta.persistence.EntityManager;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.data.jpa.test.autoconfigure.DataJpaTest;

import java.util.Optional;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,20 @@
import org.mockito.Mockito;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.servlet.MockMvc;

import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.flash;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;

@WebMvcTest(CompanyController.class)
@SpringBootTest
@AutoConfigureMockMvc
class CompanyControllerTest {

@Autowired
Expand All @@ -43,7 +45,6 @@ class CompanyControllerTest {
private ItemService itemService;

@Test
@WithMockUser(roles = {"ADMIN"})
void testAddConfirm_withValidInput_shouldRedirectToAll() throws Exception {
CompanyAddBindingModel validModel = new CompanyAddBindingModel();
validModel.setName("Valid Name");
Expand All @@ -59,7 +60,7 @@ void testAddConfirm_withValidInput_shouldRedirectToAll() throws Exception {
Mockito.when(companyService.getCompanyByUniqueIdentifier(validModel.getUniqueIdentifier())).thenReturn(null);
Mockito.when(modelMapper.map(validModel, CompanyServiceModel.class)).thenReturn(serviceModel);

mockMvc.perform(post("/company/add")
mockMvc.perform(post("/company/add").with(user("test-admin").roles("ADMIN"))
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.with(csrf())
.param("name", validModel.getName())
Expand All @@ -69,7 +70,6 @@ void testAddConfirm_withValidInput_shouldRedirectToAll() throws Exception {
}

@Test
@WithMockUser(roles = {"ADMIN"})
void testAddConfirm_withNameConflict_shouldRedirectToAdd() throws Exception {
CompanyAddBindingModel conflictingModel = new CompanyAddBindingModel();
conflictingModel.setName("Existing Company");
Expand All @@ -81,7 +81,7 @@ void testAddConfirm_withNameConflict_shouldRedirectToAdd() throws Exception {

Mockito.when(companyService.getCompanyByName(conflictingModel.getName())).thenReturn(conflictingServiceModel);

mockMvc.perform(post("/company/add")
mockMvc.perform(post("/company/add").with(user("test-admin").roles("ADMIN"))
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.with(csrf())
.param("name", conflictingModel.getName())
Expand All @@ -93,7 +93,6 @@ void testAddConfirm_withNameConflict_shouldRedirectToAdd() throws Exception {
}

@Test
@WithMockUser(roles = {"ADMIN"})
void testAddConfirm_withUniqueIdentifierConflict_shouldRedirectToAdd() throws Exception {
CompanyAddBindingModel conflictingModel = new CompanyAddBindingModel();
conflictingModel.setName("New Company");
Expand All @@ -106,7 +105,7 @@ void testAddConfirm_withUniqueIdentifierConflict_shouldRedirectToAdd() throws Ex
Mockito.when(companyService.getCompanyByName(conflictingModel.getName())).thenReturn(null);
Mockito.when(companyService.getCompanyByUniqueIdentifier(conflictingModel.getUniqueIdentifier())).thenReturn(conflictingServiceModel);

mockMvc.perform(post("/company/add")
mockMvc.perform(post("/company/add").with(user("test-admin").roles("ADMIN"))
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.with(csrf())
.param("name", conflictingModel.getName())
Expand All @@ -118,9 +117,8 @@ void testAddConfirm_withUniqueIdentifierConflict_shouldRedirectToAdd() throws Ex
}

@Test
@WithMockUser(roles = {"ADMIN"})
void testAddConfirm_withValidationErrors_shouldRedirectToAdd() throws Exception {
mockMvc.perform(post("/company/add")
mockMvc.perform(post("/company/add").with(user("test-admin").roles("ADMIN"))
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.with(csrf())
.param("name", "")
Expand Down
Loading
Loading