Search code examples
spring-bootunit-testingspring-securityspring-data-jpajunit5

I am testing my controllers in a project i have and im trying to use the annonation @WithMockUser but when i use it the status is always 403 forbidden


I'm encountering an issue with my Spring Security configuration where even though I've granted access to an endpoint using permitAll(), I'm still receiving a 403 Forbidden response. Here are the relevant classes: ```

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfiguration {
<kbd>your text</kbd>
    private final JwtAuthenticationFilter jwtAuthFilter;
    private final AuthenticationProvider authenticationProvider;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

        http
            .csrf((httpSecurityCsrfConfigurer) -> httpSecurityCsrfConfigurer.disable())
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/projects/create").hasAuthority("ADMIN_CREATE")
                .requestMatchers(GET, "api/departments/**", "api/projects/**", "api/employees/**").hasAnyAuthority("ADMIN_READ", "USER_READ", "ROLE_ADMIN_CREATE")
                .requestMatchers(POST, "api/departments/**", "api/projects/**", "api/employees/**").hasAuthority("ADMIN_CREATE")
                .requestMatchers(PUT, "api/departments/**", "api/projects/**", "api/employees/**").hasAuthority("ADMIN_UPDATE")
                .requestMatchers(DELETE, "api/departments/**", "api/projects/**", "api/employees/**").hasAuthority("ADMIN_DELETE")
                .requestMatchers("api/roles").hasAuthority("ADMIN_MANAGE")
                .requestMatchers("api/permissions").hasAuthority("ADMIN_MANAGE")
                .requestMatchers("/swagger-ui.html").permitAll()
                .requestMatchers("/swagger-ui/**").permitAll()
                .requestMatchers("/api-docs/**").permitAll()
                .requestMatchers("api/v1/**").permitAll()
                .anyRequest().authenticated()
            )
            .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .authenticationProvider(authenticationProvider)
            .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }
}

package com.example.demo.config;

import com.example.demo.dataproviders.services.impl.JwtService;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final JwtService jwtService;
    private final UserDetailsService userDetailsService;
    @Override
    protected void doFilterInternal(
            @NotNull HttpServletRequest request,
            @NotNull HttpServletResponse response,
            @NotNull FilterChain filterChain
    ) throws ServletException, IOException {
        final String authHeader = request.getHeader("Authorization");
        final String userEmail;
        final String jwt;
        if (authHeader == null || !authHeader.startsWith("Bearer ")){
            filterChain.doFilter(request,response);
            return;
        }
        jwt=authHeader.substring(7);
        userEmail=jwtService.extractUsername(jwt);
        if (userEmail != null &&
                SecurityContextHolder.getContext().getAuthentication() == null){
            UserDetails userDetails = this.userDetailsService.loadUserByUsername(userEmail);
            if (jwtService.isTokenValid(jwt,userDetails)){
                UsernamePasswordAuthenticationToken authToken =
                        new UsernamePasswordAuthenticationToken(userDetails,null,
                                userDetails.getAuthorities());

                authToken.setDetails(
                        new WebAuthenticationDetailsSource().buildDetails(request)
                );
                SecurityContextHolder.getContext().setAuthentication(authToken);
            }

        }
        filterChain.doFilter(request,response);

    }

//    @Override
//    protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
//
//        boolean notFiltered = request.getServletPath().equals("/departments") || request.getServletPath().equals("/projects")
//                || request.getServletPath().contains("/swagger-ui") || request.getServletPath().contains("/api-docs") || request.getServletPath().equals("/login");
//
//        return notFiltered;
//    }
}


package com.example.demo.controller;

import com.example.demo.dataproviders.dto.request.ProjectDTO;
import com.example.demo.dataproviders.dto.request.ProjectEmployeeDTO;
import com.example.demo.dataproviders.entities.Projects;
import com.example.demo.dataproviders.services.ProjectService;
import com.example.demo.dataproviders.services.impl.JwtService;
import com.example.demo.endpoints.rest.ProjectsController;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.hamcrest.CoreMatchers;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentMatchers;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithAnonymousUser;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;

import java.util.Arrays;
import java.util.List;

import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.when;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@WebMvcTest(controllers = ProjectsController.class)
@AutoConfigureMockMvc(addFilters = true)
@ExtendWith(MockitoExtension.class)
public class ProjectControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private ProjectService projectService;

    @MockBean
    private JwtService jwtService;

    @Autowired
    private ObjectMapper objectMapper;

    private Projects project;
    private ProjectDTO projectDTO1;
    private ProjectDTO projectDTO2;
    private ProjectEmployeeDTO projectEmployeeDTO;

    @BeforeEach
    public void init() {
        project = Projects.builder()
                .project_id(1)
                .project_name("Spring")
                .build();

        projectDTO1 = ProjectDTO.builder()
                .project_id(1)
                .project_name("Spring")
                .build();

        projectDTO2 = ProjectDTO.builder()
                .project_id(2)
                .project_name("Hibernate")
                .build();

        projectEmployeeDTO = ProjectEmployeeDTO.builder()
                .projectId(1)
                .employeeId(1)
                .build();
    }

    @Test
    @WithMockUser(authorities = {"ADMIN_CREATE"})
//    @WithAnonymousUser
    public void ProjectController_createProject_ReturnCreated() throws Exception {
        given(projectService.createProject(ArgumentMatchers.any(ProjectDTO.class)))
                .willReturn(projectDTO1);

        ResultActions response = mockMvc.perform(post("/api/projects/create")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(projectDTO1)));

        response.andExpect(status().isOk())
//                .andExpect(MockMvcResultMatchers.content().contentType("text/plain;charset=UTF-8"))
                .andExpect(MockMvcResultMatchers.content().string("Projekti me id: " + projectDTO1.getProject_id() + " u krijua:" + projectDTO1));
    }

    @Test
    @WithMockUser(authorities = {"ADMIN_READ"})
    public void ProjectController_getAllProjects_ReturnListOfProjectDTO() throws Exception {
        List<ProjectDTO> projectDTOList = Arrays.asList(projectDTO1, projectDTO2);

        given(projectService.getAllProjects()).willReturn(Arrays.asList(projectDTO1, projectDTO2));

        ResultActions response = mockMvc.perform(get("/api/projects")
                .contentType(MediaType.APPLICATION_JSON));

        response.andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON))
                .andExpect(MockMvcResultMatchers.jsonPath("$.length()").exists())
                .andExpect(MockMvcResultMatchers.jsonPath("$.length()", CoreMatchers.is(projectDTOList.size())))
                .andExpect(MockMvcResultMatchers.jsonPath("$[0].project_name", CoreMatchers.is("Spring")))
                .andExpect(MockMvcResultMatchers.jsonPath("$[1].project_name", CoreMatchers.is("Hibernate")));
    }

    @Test
    @WithMockUser(authorities = {"ADMIN_READ"})
    public void ProjectController_getProjectById_ReturnProjectDTO() throws Exception {
        given(projectService.getProjectById(1)).willReturn(projectDTO1);

        mockMvc.perform(get("/api/projects/1"))
                .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.project_id").value(1))
                .andExpect(MockMvcResultMatchers.jsonPath("$.project_name").value("Spring"));
    }

    @Test
    @WithMockUser(authorities = {"ADMIN_UPDATE"})
    public void ProjectController_updateProject_ReturnUpdatedProjectDTO() throws Exception {
        ProjectDTO updatedProject = ProjectDTO.builder()
                .project_id(1)
                .project_name("Updated Spring")
                .build();

        given(projectService.updateProject(ArgumentMatchers.any(ProjectDTO.class), ArgumentMatchers.anyInt()))
                .willReturn(updatedProject);

        mockMvc.perform(put("/api/projects/1")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(updatedProject)))
                .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.project_id").value(1))
                .andExpect(MockMvcResultMatchers.jsonPath("$.project_name").value("Updated Spring"));
    }

    @Test
    @WithMockUser(authorities = {"ADMIN_DELETE"})
    public void ProjectController_deleteProject_ReturnString() throws Exception {
        given(projectService.deleteProject(1)).willReturn(1);

        mockMvc.perform(delete("/api/projects/1"))
                .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.content().string("Projekti me id: 1 u fshi"));
    }

    @Test
    @WithMockUser(authorities = {"ADMIN_CREATE"})
    public void ProjectController_addEmployeeToProject_ReturnConfirmationMessage() throws Exception {
        given(projectService.addEmployeeToProject(1, 1)).willReturn(projectEmployeeDTO);

        mockMvc.perform(post("/api/projects/addEmployee")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(projectEmployeeDTO)))
                .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.content().contentType("text/plain;charset=UTF-8"))
                .andExpect(MockMvcResultMatchers.content().string("Employee me id: 1 u shtua ne projektin me id: 1"));
    }
}

I've tried every method but only the get methods work, and for the others i  get a `403 Forbidden` response. It seems like there might be an issue with the filters. Can anyone help me troubleshoot this?

Solution

  • @Autowired
    private WebApplicationContext webApplicationContext
    
    @Before()
    public void setup()
    {
        //
    
    Init MockMvc Object and build
        mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
    }
    

    i just had to add this ,this was the answer of a guy i found here in stack (XuanGang i think it was this guys answer)