diff --git a/src/main/java/org/example/visacasemanagementsystem/ApplicationViewController.java b/src/main/java/org/example/visacasemanagementsystem/ApplicationViewController.java index 793ff18..4385fe6 100644 --- a/src/main/java/org/example/visacasemanagementsystem/ApplicationViewController.java +++ b/src/main/java/org/example/visacasemanagementsystem/ApplicationViewController.java @@ -8,6 +8,12 @@ @Controller public class ApplicationViewController { + + @GetMapping("/") + public String index() { + return "redirect:/dashboard"; + } + @GetMapping("/dashboard") public String dashboard(@AuthenticationPrincipal UserPrincipal principal) { if (principal == null) { diff --git a/src/main/java/org/example/visacasemanagementsystem/config/SecurityConfig.java b/src/main/java/org/example/visacasemanagementsystem/config/SecurityConfig.java index f0d6685..208e14b 100644 --- a/src/main/java/org/example/visacasemanagementsystem/config/SecurityConfig.java +++ b/src/main/java/org/example/visacasemanagementsystem/config/SecurityConfig.java @@ -29,12 +29,16 @@ public SecurityConfig(UserDetailsService userDetailsService) { public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth + .requestMatchers("/").permitAll() .requestMatchers("/user/signup").permitAll() .requestMatchers("/user/login").permitAll() + .requestMatchers("/logout").permitAll() .requestMatchers("/dashboard").authenticated() + .requestMatchers("/profile/**").authenticated() + .requestMatchers("/visas/**").authenticated() + .requestMatchers("/api/comments/**").authenticated() .requestMatchers("/**/admin").hasRole("ADMIN") .requestMatchers("/**/applicant").hasRole("USER") - //TODO: requestMatchers for /**/{userId} endpoints, etc. .anyRequest().hasRole("SYSADMIN") ) .headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin)) diff --git a/src/main/java/org/example/visacasemanagementsystem/user/controller/UserViewController.java b/src/main/java/org/example/visacasemanagementsystem/user/controller/UserViewController.java index 362dd2d..590563b 100644 --- a/src/main/java/org/example/visacasemanagementsystem/user/controller/UserViewController.java +++ b/src/main/java/org/example/visacasemanagementsystem/user/controller/UserViewController.java @@ -11,6 +11,7 @@ import org.example.visacasemanagementsystem.user.service.UserService; import org.example.visacasemanagementsystem.visa.dto.VisaDTO; import org.example.visacasemanagementsystem.visa.service.VisaService; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; @@ -21,6 +22,7 @@ import java.util.List; import java.util.Objects; +@PreAuthorize("isAuthenticated()") @Controller public class UserViewController { private final VisaService visaService; @@ -36,6 +38,7 @@ public UserViewController(VisaService visaService, } // Signup form for new users, accessible to all + @PreAuthorize("permitAll()") @GetMapping("/user/signup") public String userSignupForm(Model model) { return "user/signup"; @@ -44,6 +47,7 @@ public String userSignupForm(Model model) { // Posting a successful user creation then redirecting to the applicant dashboard since only applicants can be // created through the signup process. Admins or Sysadmins are created from applicant users by given authorization // from a sysadmin. + @PreAuthorize("permitAll()") @PostMapping("/user/signup") public String createUser(@RequestParam String fullName, @RequestParam String email, @@ -62,6 +66,7 @@ public String createUser(@RequestParam String fullName, } // Login page + @PreAuthorize("permitAll()") @GetMapping("/user/login") public String userLoginForm(){ return "user/login"; @@ -122,16 +127,17 @@ public String updateProfile(@AuthenticationPrincipal UserPrincipal principal, } // A list view of users only available to sysadmins + @PreAuthorize("hasRole('SYSADMIN')") @GetMapping("/user/list") public String userListView(@AuthenticationPrincipal UserPrincipal principal, Model model) { - userService.validateSysAdmin(principal); List allUsers = userService.findAll(); model.addAttribute("name", principal.getFullName()); model.addAttribute("users", allUsers); return "user/list"; } + @PreAuthorize("hasRole('USER')") @GetMapping("/dashboard/applicant") public String applicantDashboard(@AuthenticationPrincipal UserPrincipal principal, Model model) { @@ -141,22 +147,22 @@ public String applicantDashboard(@AuthenticationPrincipal UserPrincipal principa return "dashboard/applicant"; } + @PreAuthorize("hasRole('ADMIN')") @GetMapping("/dashboard/admin") public String adminDashboard(@AuthenticationPrincipal UserPrincipal principal, Model model) { - userService.validateAdmin(principal); List assignedCases = visaService.findVisasByHandlerId(principal.getUserId()); - List unassignedCases = visaService.findVisaByStatus("UNASSIGNED"); + List unassignedCases = visaService.findVisaByStatus("SUBMITTED"); model.addAttribute("name", principal.getFullName()); model.addAttribute("assignedCases", assignedCases); model.addAttribute("unassignedCases", unassignedCases); return "dashboard/admin"; } + @PreAuthorize("hasRole('SYSADMIN')") @GetMapping("/dashboard/sysadmin") public String sysAdminDashboard(@AuthenticationPrincipal UserPrincipal principal, Model model) { - userService.validateSysAdmin(principal); List allUsers = userService.findAll(); List recentLogs = auditService.findAll(); model.addAttribute("name", principal.getFullName()); diff --git a/src/main/java/org/example/visacasemanagementsystem/user/service/UserService.java b/src/main/java/org/example/visacasemanagementsystem/user/service/UserService.java index c85ef87..2fc1ffe 100644 --- a/src/main/java/org/example/visacasemanagementsystem/user/service/UserService.java +++ b/src/main/java/org/example/visacasemanagementsystem/user/service/UserService.java @@ -12,6 +12,7 @@ import org.example.visacasemanagementsystem.user.repository.UserRepository; import org.example.visacasemanagementsystem.user.security.UserPrincipal; import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -81,10 +82,9 @@ public UserDTO updateUser(UpdateUserDTO dto) { } } + @PreAuthorize("hasRole('SYSADMIN')") @Transactional - public UserDTO updateUserAuthorization(Long userId, UserAuthorization newAuth, Long requesterId) { - validateSysAdmin(requesterId); // confirm requester is SYSADMIN - + public UserDTO updateUserAuthorization(Long userId, UserAuthorization newAuth) { User user = userRepository.findById(userId) .orElseThrow(() -> new EntityNotFoundException(USER_NOT_FOUND)); @@ -93,23 +93,14 @@ public UserDTO updateUserAuthorization(Long userId, UserAuthorization newAuth, L return userMapper.toDTO(savedUser); } + @PreAuthorize("hasRole('SYSADMIN')") @Transactional - public void deleteUser(Long id, Long requesterId) { - validateSysAdmin(requesterId); // confirm requester is SYSADMIN + public void deleteUser(Long id) { User user = userRepository.findById(id) .orElseThrow(() -> new EntityNotFoundException(USER_NOT_FOUND)); userRepository.delete(user); } - private void validateSysAdmin(Long requesterId) { - User requester = userRepository.findById(requesterId) - .orElseThrow(() -> new EntityNotFoundException(USER_NOT_FOUND)); - - if (requester.getUserAuthorization() != UserAuthorization.SYSADMIN) { - throw new UnauthorizedException("User is not authorized to perform this action."); - } - } - public void validateProfileAccess(UserPrincipal principal, Long userId) { boolean isOwnProfile = principal.getUserId().equals(userId); boolean isSysAdmin = principal.getAuthorities().stream() @@ -119,24 +110,4 @@ public void validateProfileAccess(UserPrincipal principal, Long userId) { throw new UnauthorizedException("You do not have permission to edit this profile."); } } - - public void validateSysAdmin(UserPrincipal principal) { - boolean isSysAdmin = principal.getAuthorities().stream() - .anyMatch(a -> Objects.equals(a.getAuthority(), "ROLE_SYSADMIN")); - - if (!isSysAdmin) { - throw new UnauthorizedException("Only system administrators can access this page."); - } - } - - public void validateAdmin(UserPrincipal principal) { - boolean isSysAdmin = principal.getAuthorities().stream() - .anyMatch(a -> Objects.equals(a.getAuthority(), "ROLE_SYSADMIN")); - boolean isAdmin = principal.getAuthorities().stream() - .anyMatch(a -> Objects.equals(a.getAuthority(), "ROLE_ADMIN")); - - if (!isSysAdmin && !isAdmin) { - throw new UnauthorizedException("Only administrators or system administrators can access this page."); - } - } } diff --git a/src/main/java/org/example/visacasemanagementsystem/visa/controller/VisaViewController.java b/src/main/java/org/example/visacasemanagementsystem/visa/controller/VisaViewController.java index c55e419..046c037 100644 --- a/src/main/java/org/example/visacasemanagementsystem/visa/controller/VisaViewController.java +++ b/src/main/java/org/example/visacasemanagementsystem/visa/controller/VisaViewController.java @@ -6,20 +6,22 @@ import org.example.visacasemanagementsystem.exception.UnauthorizedException; import org.example.visacasemanagementsystem.user.UserAuthorization; import org.example.visacasemanagementsystem.user.dto.UserDTO; +import org.example.visacasemanagementsystem.user.security.UserPrincipal; import org.example.visacasemanagementsystem.user.service.UserService; import org.example.visacasemanagementsystem.visa.VisaType; import org.example.visacasemanagementsystem.visa.dto.CreateVisaDTO; import org.example.visacasemanagementsystem.visa.dto.UpdateVisaDTO; import org.example.visacasemanagementsystem.visa.dto.VisaDTO; import org.example.visacasemanagementsystem.visa.service.VisaService; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; -import org.springframework.web.servlet.mvc.support.RedirectAttributes; - import java.util.List; +@PreAuthorize("isAuthenticated()") @Controller @RequestMapping("/visas") public class VisaViewController { @@ -35,20 +37,17 @@ public VisaViewController(VisaService visaService, CommentService commentService } @GetMapping("/dashboard") - public String showDashboard(@RequestParam Long currentUserId, Model model) { - // Find current user - UserDTO user = userService.findById(currentUserId) + public String showDashboard(@AuthenticationPrincipal UserPrincipal principal, Model model) { + UserDTO user = userService.findById(principal.getUserId()) .orElseThrow(() -> new EntityNotFoundException("User not found.")); - List visas; - // If the current user = ADMIN/SYSADMIN show everything - if (user.userAuthorization() == UserAuthorization.ADMIN || + if ( user.userAuthorization() == UserAuthorization.ADMIN || user.userAuthorization() == UserAuthorization.SYSADMIN) { visas = visaService.findAll(); } else { // Normal user/applicant can only se their own visa applications - visas = visaService.findVisasByApplicant(currentUserId); + visas = visaService.findVisasByApplicant(principal.getUserId()); } // Send data to Thymeleaf @@ -59,15 +58,20 @@ public String showDashboard(@RequestParam Long currentUserId, Model model) { } @GetMapping("/apply") - public String showApplyForm(@RequestParam Long currentUserId, Model model) { - UserDTO user = userService.findById(currentUserId) + public String showApplyForm(@AuthenticationPrincipal UserPrincipal principal, Model model) { + UserDTO user = userService.findById(principal.getUserId()) .orElseThrow(() -> new EntityNotFoundException("User not found.")); model.addAttribute("currentUser", user); model.addAttribute("visaTypes", VisaType.values()); if (!model.containsAttribute("createVisaDTO")) { - model.addAttribute("createVisaDTO", new CreateVisaDTO(null, "", "", null, currentUserId)); + model.addAttribute("createVisaDTO", + new CreateVisaDTO(null, + "", + "", + null, + principal.getUserId())); } return "visa/apply-form"; @@ -77,34 +81,33 @@ public String showApplyForm(@RequestParam Long currentUserId, Model model) { public String submitApplication( @Valid @ModelAttribute("createVisaDTO") CreateVisaDTO createVisaDTO, BindingResult bindingResult, - @RequestParam Long currentUserId, - RedirectAttributes redirectAttributes, + @AuthenticationPrincipal UserPrincipal principal, Model model) { if (bindingResult.hasErrors()) { - prepareApplyModel(currentUserId, model); + prepareApplyModel(principal.getUserId(), model); return "visa/apply-form"; } try { - visaService.applyForVisa(createVisaDTO, currentUserId); + visaService.applyForVisa(createVisaDTO, principal.getUserId()); } catch (IllegalArgumentException e) { bindingResult.rejectValue("travelDate", "error.travelDate", e.getMessage()); - prepareApplyModel(currentUserId, model); + prepareApplyModel(principal.getUserId(), model); return "visa/apply-form"; } - return "redirect:/visas/dashboard?currentUserId=" + currentUserId; + return "redirect:/visas/dashboard"; } @GetMapping("/{id}/edit") - public String showEditForm(@PathVariable Long id, @RequestParam Long currentUserId, Model model) { - UserDTO userDTO = userService.findById(currentUserId) + public String showEditForm(@PathVariable Long id, @AuthenticationPrincipal UserPrincipal principal, Model model) { + UserDTO userDTO = userService.findById(principal.getUserId()) .orElseThrow(() -> new EntityNotFoundException("User not found.")); VisaDTO visa = visaService.findVisaDtoById(id); - if (!visa.applicantId().equals(currentUserId)) { + if (!visa.applicantId().equals(principal.getUserId())) { throw new UnauthorizedException("You can only edit your own applications."); } @@ -132,11 +135,11 @@ public String processUpdate( @PathVariable Long id, @Valid @ModelAttribute ("updateVisaDto") UpdateVisaDTO updateVisaDTO, BindingResult bindingResult, - @RequestParam Long currentUserId, + @AuthenticationPrincipal UserPrincipal principal, Model model) { if (bindingResult.hasErrors()) { - prepareApplyModel(currentUserId, model); + prepareApplyModel(principal.getUserId(), model); model.addAttribute("isEdit", true); VisaDTO visa = visaService.findVisaDtoById(id); model.addAttribute("statusInformation", visa.statusInformation()); @@ -144,7 +147,7 @@ public String processUpdate( } try { - visaService.updateVisa(id, updateVisaDTO, currentUserId); + visaService.updateVisa(id, updateVisaDTO, principal.getUserId()); } catch (IllegalArgumentException e) { if (e.getMessage().contains("date")) { bindingResult.rejectValue("travelDate", "error.travelDate", e.getMessage()); @@ -152,66 +155,67 @@ public String processUpdate( bindingResult.reject("globalError",e.getMessage()); } - prepareApplyModel(currentUserId, model); + prepareApplyModel(principal.getUserId(), model); model.addAttribute("isEdit", true); VisaDTO visa = visaService.findVisaDtoById(id); model.addAttribute("statusInformation", visa.statusInformation()); return "visa/edit-form"; } - return "redirect:/visas/" + id + "?currentUserId=" + currentUserId; + return "redirect:/visas/" + id; } + @PreAuthorize("hasRole('ADMIN')") @PostMapping("/{id}/approve") - public String approveVisa(@PathVariable Long id, @RequestParam Long currentUserId) { - visaService.approveVisa(id, currentUserId); - return "redirect:/visas/" + id + "?currentUserId=" + currentUserId; + public String approveVisa(@PathVariable Long id, @AuthenticationPrincipal UserPrincipal principal) { + visaService.approveVisa(id, principal.getUserId()); + return "redirect:/visas/" + id; } + @PreAuthorize("hasRole('ADMIN')") @PostMapping("/{id}/request-info") public String requestMoreInformation(@PathVariable Long id, - @RequestParam Long currentUserId, + @AuthenticationPrincipal UserPrincipal principal, @RequestParam String reason) { - visaService.requestMoreInformation(id, currentUserId, reason); + visaService.requestMoreInformation(id, principal.getUserId(), reason); - return "redirect:/visas/" + id + "?currentUserId=" + currentUserId; + return "redirect:/visas/" + id; } - + @PreAuthorize("hasRole('ADMIN')") @PostMapping("/{id}/reject") public String rejectVisa(@PathVariable Long id, - @RequestParam Long currentUserId, + @AuthenticationPrincipal UserPrincipal principal, @RequestParam String reason) { - visaService.rejectVisa(id, currentUserId, reason); - return "redirect:/visas/" + id + "?currentUserId=" + currentUserId; + visaService.rejectVisa(id, principal.getUserId(), reason); + return "redirect:/visas/" + id; } - + @PreAuthorize("hasRole('ADMIN')") @PostMapping("/{id}/assign") - public String assignCaseToHandler(@PathVariable Long id, @RequestParam Long currentUserId) { - visaService.assignHandler(id, currentUserId); - return "redirect:/visas/" + id + "?currentUserId=" + currentUserId; + public String assignCaseToHandler(@PathVariable Long id, @AuthenticationPrincipal UserPrincipal principal) { + visaService.assignHandler(id, principal.getUserId()); + return "redirect:/visas/" + id; } @GetMapping("/{id}") - public String viewDetails(@PathVariable Long id, @RequestParam Long currentUserId, Model model) { + public String viewDetails(@PathVariable Long id, @AuthenticationPrincipal UserPrincipal principal, Model model) { // Get visa VisaDTO visa = visaService.findVisaDtoById(id); // Get user - UserDTO user = userService.findById(currentUserId) + UserDTO user = userService.findById(principal.getUserId()) .orElseThrow(() -> new EntityNotFoundException("User not found.")); // Authorization check: regular users can only view their own applications - if (user.userAuthorization() == UserAuthorization.USER && !visa.applicantId().equals(currentUserId)) { + if (user.userAuthorization() == UserAuthorization.USER && !visa.applicantId().equals(principal.getUserId())) { throw new UnauthorizedException("You can only view your own applications."); } // Get comments var comments = commentService.getCommentsByVisaId(id); - model.addAttribute("visa", visa); model.addAttribute("comments", comments); model.addAttribute("currentUser", user); diff --git a/src/main/resources/templates/visa/apply-form.html b/src/main/resources/templates/visa/apply-form.html index a76b04c..4f9d813 100644 --- a/src/main/resources/templates/visa/apply-form.html +++ b/src/main/resources/templates/visa/apply-form.html @@ -114,7 +114,7 @@

New Application

Logged in as User

-
+
New Application - Cancel + Cancel
diff --git a/src/main/resources/templates/visa/dashboard.html b/src/main/resources/templates/visa/dashboard.html index d713d66..e179ea3 100644 --- a/src/main/resources/templates/visa/dashboard.html +++ b/src/main/resources/templates/visa/dashboard.html @@ -119,7 +119,7 @@

Visa Dashboard

diff --git a/src/test/java/org/example/visacasemanagementsystem/visa/VisaViewControllerTest.java b/src/test/java/org/example/visacasemanagementsystem/visa/VisaViewControllerTest.java index a5a8b7b..94c89bb 100644 --- a/src/test/java/org/example/visacasemanagementsystem/visa/VisaViewControllerTest.java +++ b/src/test/java/org/example/visacasemanagementsystem/visa/VisaViewControllerTest.java @@ -4,6 +4,8 @@ import org.example.visacasemanagementsystem.comment.service.CommentService; import org.example.visacasemanagementsystem.exception.UnauthorizedException; import org.example.visacasemanagementsystem.user.UserAuthorization; +import org.example.visacasemanagementsystem.user.entity.User; +import org.example.visacasemanagementsystem.user.security.UserPrincipal; import org.example.visacasemanagementsystem.user.dto.UserDTO; import org.example.visacasemanagementsystem.user.service.UserService; import org.example.visacasemanagementsystem.visa.controller.VisaViewController; @@ -15,6 +17,9 @@ import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest; +import org.springframework.security.authentication.TestingAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.servlet.MockMvc; @@ -55,8 +60,7 @@ void showDashboard_AsAdmin_ShouldReturnAllVisas() throws Exception { when(visaService.findAll()).thenReturn(List.of()); // Act & Assert - mockMvc.perform(get("/visas/dashboard") - .param("currentUserId", adminId.toString())) + mockMvc.perform(get("/visas/dashboard")) .andExpect(status().isOk()) .andExpect(view().name("visa/dashboard")) .andExpect(model().attributeExists("visas")) @@ -91,13 +95,12 @@ void showDashboard_AsUser_ShouldReturnUserVisas() throws Exception { void showApplyForm_ShouldReturnApplyViewWithEmptyDto() throws Exception { // Arrange Long userId = 1L; - UserDTO mockUser = new UserDTO(userId ,"Mock User", "mock@test.com",UserAuthorization.USER); + UserDTO mockUser = createMockUser(userId, UserAuthorization.USER); when(userService.findById(userId)).thenReturn(Optional.of(mockUser)); // Act & Assert - mockMvc.perform(get("/visas/apply") - .param("currentUserId", userId.toString())) + mockMvc.perform(get("/visas/apply")) .andExpect(status().isOk()) .andExpect(view().name("visa/apply-form")) .andExpect(model().attributeExists("currentUser")) @@ -112,6 +115,7 @@ void showApplyForm_ShouldReturnApplyViewWithEmptyDto() throws Exception { void submitApplication_WithValidData_ShouldRedirectToDashboard() throws Exception { // Arrange Long userId = 1L; + UserDTO mockUser = createMockUser(userId, UserAuthorization.USER); // Act & Assert mockMvc.perform(post("/visas/apply") @@ -122,7 +126,7 @@ void submitApplication_WithValidData_ShouldRedirectToDashboard() throws Exceptio .param("travelDate", "2026-12-01") .with(csrf())) .andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrl("/visas/dashboard?currentUserId=" + userId)); + .andExpect(redirectedUrl("/visas/dashboard")); verify(visaService).applyForVisa(any(CreateVisaDTO.class), eq(userId)); } @@ -132,7 +136,7 @@ void submitApplication_WithValidData_ShouldRedirectToDashboard() throws Exceptio void submitApplication_WithInvalidData_ShouldReturnForm() throws Exception { // Arrange Long userId = 1L; - UserDTO mockUser = new UserDTO(userId ,"Mock User", "mock@test.com",UserAuthorization.USER); + UserDTO mockUser = createMockUser(userId, UserAuthorization.USER); when(userService.findById(userId)).thenReturn(Optional.of(mockUser)); @@ -167,8 +171,7 @@ void showEditForm_AsCurrentUser_ShouldReturnEditView() throws Exception { when(visaService.findVisaDtoById(visaId)).thenReturn(createMockVisa(visaId, userId)); // Act - var result = mockMvc.perform(get("/visas/{id}/edit", visaId) - .param("currentUserId", userId.toString())); + var result = mockMvc.perform(get("/visas/{id}/edit", visaId)); // Assert result.andExpect(status().isOk()) @@ -190,8 +193,7 @@ void showEditForm_AsWrongUser_ShouldThrowUnauthorizedException() throws Exceptio when(visaService.findVisaDtoById(visaId)).thenReturn(createMockVisa(visaId, actualUserId)); // Act & Assert - mockMvc.perform(get("/visas/{id}/edit", visaId) - .param("currentUserId", loggedInUserId.toString())) + mockMvc.perform(get("/visas/{id}/edit", visaId)) .andExpect(status().isForbidden()); } @@ -201,7 +203,7 @@ void processUpdate_Success_ShouldRedirectToDetails() throws Exception { // Arrange Long visaId = 100L; Long userId = 1L; - String expectedUrl = "/visas/" + visaId + "?currentUserId=" + userId; + String expectedUrl = "/visas/" + visaId; when(userService.findById(userId)).thenReturn(Optional.of(createMockUser(userId, UserAuthorization.USER))); when(visaService.findVisaDtoById(visaId)).thenReturn(createMockVisa(visaId, userId)); @@ -258,6 +260,7 @@ void processUpdate_Unauthorized_ShouldReturnForbiddenStatus() throws Exception { // Arrange Long visaId = 100L; Long userId = 1L; + UserDTO mockUser = createMockUser(userId, UserAuthorization.USER); doThrow(new UnauthorizedException("You are not authorized to update this application.")) .when(visaService).updateVisa(eq(visaId), any(UpdateVisaDTO.class), eq(userId)); @@ -316,17 +319,17 @@ void approveVisa_AsAdmin_ShouldRedirectToDetailsView() throws Exception { // Arrange Long visaId = 100L; Long adminId = 1L; + UserDTO mockAdmin = createMockUser(adminId, UserAuthorization.ADMIN); when(visaService.approveVisa(visaId,adminId)).thenReturn(null); // Act var result = mockMvc.perform(post("/visas/{id}/approve", visaId) - .param("currentUserId", adminId.toString()) .with(csrf())); // Assert result.andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrl("/visas/" + visaId + "?currentUserId=" + adminId)); + .andExpect(redirectedUrl("/visas/" + visaId)); verify(visaService, times(1)).approveVisa(visaId,adminId); } @@ -337,11 +340,11 @@ void assignCaseToHandler_Success_ShouldRedirectToDetails() throws Exception { // Arrange Long visaId = 100L; Long adminId = 1L; - String expectedUrl = "/visas/" + visaId + "?currentUserId=" + adminId; + UserDTO mockAdmin = createMockUser(adminId, UserAuthorization.ADMIN); + String expectedUrl = "/visas/" + visaId; // Act & Assert mockMvc.perform(post("/visas/{id}/assign",visaId) - .param("currentUserId", adminId.toString()) .with(csrf())) .andExpect(status().is3xxRedirection()) .andExpect(redirectedUrl(expectedUrl)); @@ -356,12 +359,12 @@ void requestMoreInformation_Success_ShouldRedirectToDetails() throws Exception { // Arrange Long visaId = 100L; Long adminId = 1L; + UserDTO mockAdmin = createMockUser(adminId, UserAuthorization.ADMIN); String reason = "Please upload a clearer image of your passport"; - String expectedUrl = "/visas/" + visaId + "?currentUserId=" + adminId; + String expectedUrl = "/visas/" + visaId; // Act & Assert mockMvc.perform(post("/visas/{id}/request-info", visaId) - .param("currentUserId", adminId.toString()) .param("reason", reason) .with(csrf())) .andExpect(status().is3xxRedirection()) @@ -377,19 +380,19 @@ void rejectVisa_AsAdmin_ShouldRedirectToDetailsView() throws Exception { // Arrange Long visaId = 100L; Long adminId = 1L; + UserDTO mockAdmin = createMockUser(adminId, UserAuthorization.ADMIN); String reason = "Missing details about your purpose of travel"; when(visaService.rejectVisa(visaId,adminId,reason)).thenReturn(null); // Act var result = mockMvc.perform(post("/visas/{id}/reject", visaId) - .param("currentUserId", adminId.toString()) .param("reason", reason) .with(csrf())); // Assert result.andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrl("/visas/" + visaId + "?currentUserId=" + adminId)); + .andExpect(redirectedUrl("/visas/" + visaId)); verify(visaService, times(1)).rejectVisa(visaId,adminId,reason); @@ -401,20 +404,19 @@ void viewDetails_AsCurrentUser_ShouldReturnDetailViewWithComments() throws Excep // Arrange Long visaId = 100L; Long userId = 1L; + UserDTO mockUser = createMockUser(userId, UserAuthorization.USER); - UserDTO mockUser = new UserDTO(userId ,"Mock User", "mock@test.com",UserAuthorization.USER); when(userService.findById(userId)).thenReturn(Optional.of(mockUser)); VisaDTO mockVisa = createMockVisa(visaId, userId); when(visaService.findVisaDtoById(visaId)).thenReturn(mockVisa); - var mockComments = List.of(new CommentDTO(1L,100L,"Admin", "Looks god!", LocalDateTime.now())); + var mockComments = List.of(new CommentDTO(1L,100L,"Admin", "Looks good!", LocalDateTime.now())); when(commentService.getCommentsByVisaId(visaId)).thenReturn(mockComments); // Act - var result = mockMvc.perform(get("/visas/{id}", visaId) - .param("currentUserId", userId.toString())); + var result = mockMvc.perform(get("/visas/{id}", visaId)); // Assert result.andExpect(status().isOk()) @@ -434,7 +436,7 @@ void viewDetails_AsWrongUser_ShouldThrowUnauthorizedException() throws Exception Long hackerId = 1L; Long userId = 99L; - UserDTO hacker = new UserDTO(hackerId, "Hacker", "hacker@test.com",UserAuthorization.USER); + UserDTO hacker = createMockUser(hackerId, UserAuthorization.USER); VisaDTO othersVisa = createMockVisa(visaId, userId); @@ -442,8 +444,7 @@ void viewDetails_AsWrongUser_ShouldThrowUnauthorizedException() throws Exception when(visaService.findVisaDtoById(visaId)).thenReturn(othersVisa); // Act & Assert - mockMvc.perform(get("/visas/{id}", visaId) - .param("currentUserId", hackerId.toString())) + mockMvc.perform(get("/visas/{id}", visaId)) .andExpect(status().isForbidden()); } @@ -456,16 +457,15 @@ void viewDetails_AsAdmin_ShouldAllowViewingOtherVisas() throws Exception { Long adminId = 50L; Long applicantId = 1L; - UserDTO admin = new UserDTO(adminId, "Admin", "admin@test.com", UserAuthorization.ADMIN); + UserDTO mockAdmin = createMockUser(adminId, UserAuthorization.ADMIN); VisaDTO applicantVisa = createMockVisa(visaId, applicantId); - when(userService.findById(adminId)).thenReturn(Optional.of(admin)); + when(userService.findById(adminId)).thenReturn(Optional.of(mockAdmin)); when(visaService.findVisaDtoById(visaId)).thenReturn(applicantVisa); when(commentService.getCommentsByVisaId(visaId)).thenReturn(List.of()); // Act & Assert - mockMvc.perform(get("/visas/{id}", visaId) - .param("currentUserId", adminId.toString())) + mockMvc.perform(get("/visas/{id}", visaId)) .andExpect(status().isOk()) .andExpect(view().name("visa/details")); @@ -473,6 +473,16 @@ void viewDetails_AsAdmin_ShouldAllowViewingOtherVisas() throws Exception { // Helper methods private UserDTO createMockUser(Long id, UserAuthorization role) { + User testUser = new User(); + testUser.setId(id); + testUser.setUsername("test@test.com"); + testUser.setEmail("test@test.com"); + testUser.setPassword("password"); + testUser.setUserAuthorization(role); + UserPrincipal principal = new UserPrincipal(testUser); + Authentication authentication = new TestingAuthenticationToken(principal, "password", principal.getAuthorities()); + SecurityContextHolder.getContext().setAuthentication(authentication); + return new UserDTO(id,"Test User", "test@test.com", role); }