BAEL-1918 Spring StrictHttpFirewall and RequestRejectedException (#11265)
* BAEL-1918: Added code to for RequestRejectedException and StrictHttpFirewall tutorial * BAEL-1918: Added code to for RequestRejectedException and StrictHttpFirewall tutorial * BAEL:1918 - Modifed the code to accomodate comments from Michal * BAEL:1918 - Modifed the code to accomodate comments from Michal
This commit is contained in:
		
							parent
							
								
									102f09fc6c
								
							
						
					
					
						commit
						be966d6d62
					
				| @ -1,7 +1,7 @@ | |||||||
| <?xml version="1.0" encoding="UTF-8"?> | <?xml version="1.0" encoding="UTF-8"?> | ||||||
| <project xmlns="http://maven.apache.org/POM/4.0.0" | <project xmlns="http://maven.apache.org/POM/4.0.0" | ||||||
|     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||||||
|     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||||||
|     <modelVersion>4.0.0</modelVersion> |     <modelVersion>4.0.0</modelVersion> | ||||||
|     <artifactId>spring-security-web-boot-3</artifactId> |     <artifactId>spring-security-web-boot-3</artifactId> | ||||||
|     <version>0.0.1-SNAPSHOT</version> |     <version>0.0.1-SNAPSHOT</version> | ||||||
| @ -25,6 +25,20 @@ | |||||||
|             <groupId>org.springframework.boot</groupId> |             <groupId>org.springframework.boot</groupId> | ||||||
|             <artifactId>spring-boot-starter-web</artifactId> |             <artifactId>spring-boot-starter-web</artifactId> | ||||||
|         </dependency> |         </dependency> | ||||||
|  |         <dependency> | ||||||
|  |             <groupId>org.springframework.boot</groupId> | ||||||
|  |             <artifactId>spring-boot-starter-test</artifactId> | ||||||
|  |             <scope>test</scope> | ||||||
|  |         </dependency> | ||||||
|  |         <dependency> | ||||||
|  |             <groupId>org.apache.commons</groupId> | ||||||
|  |             <artifactId>commons-lang3</artifactId> | ||||||
|  |         </dependency> | ||||||
|  |         <dependency> | ||||||
|  |             <groupId>org.springframework.security</groupId> | ||||||
|  |             <artifactId>spring-security-test</artifactId> | ||||||
|  |             <scope>test</scope> | ||||||
|  |         </dependency> | ||||||
|     </dependencies> |     </dependencies> | ||||||
| 
 | 
 | ||||||
| </project> | </project> | ||||||
| @ -0,0 +1,15 @@ | |||||||
|  | package com.baeldung.httpfirewall; | ||||||
|  | 
 | ||||||
|  | import org.springframework.boot.SpringApplication; | ||||||
|  | import org.springframework.boot.autoconfigure.SpringBootApplication; | ||||||
|  | 
 | ||||||
|  | @SpringBootApplication | ||||||
|  | public class HttpFirewallApplication { | ||||||
|  | 
 | ||||||
|  |     public static void main(String[] args) { | ||||||
|  |         SpringApplication application = new SpringApplication(HttpFirewallApplication.class); | ||||||
|  |         application.setAdditionalProfiles("httpfirewall"); | ||||||
|  |         application.run(args); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -0,0 +1,48 @@ | |||||||
|  | package com.baeldung.httpfirewall; | ||||||
|  | 
 | ||||||
|  | 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.WebSecurityConfigurerAdapter; | ||||||
|  | import org.springframework.security.web.firewall.HttpFirewall; | ||||||
|  | import org.springframework.security.web.firewall.HttpStatusRequestRejectedHandler; | ||||||
|  | import org.springframework.security.web.firewall.RequestRejectedHandler; | ||||||
|  | import org.springframework.security.web.firewall.StrictHttpFirewall; | ||||||
|  | 
 | ||||||
|  | import java.util.Arrays; | ||||||
|  | 
 | ||||||
|  | @Configuration | ||||||
|  | public class HttpFirewallConfiguration extends WebSecurityConfigurerAdapter { | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     protected void configure(HttpSecurity http) throws Exception { | ||||||
|  |         //@formatter:off | ||||||
|  |         http | ||||||
|  |           .csrf() | ||||||
|  |           .disable() | ||||||
|  |           .authorizeRequests() | ||||||
|  |           .antMatchers("/error") | ||||||
|  |           .permitAll() | ||||||
|  |           .anyRequest() | ||||||
|  |           .authenticated() | ||||||
|  |           .and() | ||||||
|  |           .httpBasic(); | ||||||
|  |         //@formatter:on | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Bean | ||||||
|  |     public HttpFirewall configureFirewall() { | ||||||
|  |         StrictHttpFirewall strictHttpFirewall = new StrictHttpFirewall(); | ||||||
|  |         strictHttpFirewall.setAllowedHttpMethods(Arrays.asList("GET", "POST", "DELETE", "OPTIONS")); // Allow only HTTP GET, POST, DELETE and OPTIONS methods | ||||||
|  |         return strictHttpFirewall; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* | ||||||
|  |      Use this bean if you are using Spring Security 5.4 and above | ||||||
|  |      */ | ||||||
|  |     @Bean | ||||||
|  |     public RequestRejectedHandler requestRejectedHandler() { | ||||||
|  |         return new HttpStatusRequestRejectedHandler(); // Default status code is 400. Can be customized | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -0,0 +1,59 @@ | |||||||
|  | package com.baeldung.httpfirewall.api; | ||||||
|  | 
 | ||||||
|  | import com.baeldung.httpfirewall.model.Response; | ||||||
|  | import com.baeldung.httpfirewall.model.User; | ||||||
|  | import com.baeldung.httpfirewall.service.UserServiceImpl; | ||||||
|  | import org.apache.commons.lang3.StringUtils; | ||||||
|  | import org.springframework.http.HttpStatus; | ||||||
|  | import org.springframework.http.ResponseEntity; | ||||||
|  | import org.springframework.web.bind.annotation.DeleteMapping; | ||||||
|  | import org.springframework.web.bind.annotation.GetMapping; | ||||||
|  | import org.springframework.web.bind.annotation.PathVariable; | ||||||
|  | import org.springframework.web.bind.annotation.PostMapping; | ||||||
|  | import org.springframework.web.bind.annotation.RequestBody; | ||||||
|  | import org.springframework.web.bind.annotation.RequestMapping; | ||||||
|  | import org.springframework.web.bind.annotation.RestController; | ||||||
|  | import org.springframework.web.server.ResponseStatusException; | ||||||
|  | 
 | ||||||
|  | import java.net.URI; | ||||||
|  | import java.util.ArrayList; | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.UUID; | ||||||
|  | 
 | ||||||
|  | @RestController | ||||||
|  | @RequestMapping("/api/v1/users") | ||||||
|  | public class UserApi { | ||||||
|  | 
 | ||||||
|  |     private final UserServiceImpl userServiceImpl; | ||||||
|  | 
 | ||||||
|  |     public UserApi(UserServiceImpl userServiceImpl) { | ||||||
|  |         this.userServiceImpl = userServiceImpl; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @PostMapping | ||||||
|  |     public ResponseEntity<Response> createUser(@RequestBody User user) { | ||||||
|  |         if (StringUtils.isBlank(user.getId())) { | ||||||
|  |             user.setId(UUID.randomUUID().toString()); | ||||||
|  |         } | ||||||
|  |         userServiceImpl.saveUser(user); | ||||||
|  |         Response response = new Response(HttpStatus.CREATED.value(), "User created successfully", System.currentTimeMillis()); | ||||||
|  |         URI location = URI.create("/users/" + user.getId()); | ||||||
|  |         return ResponseEntity.created(location).body(response); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @GetMapping("/{userId}") | ||||||
|  |     public User getUser(@PathVariable("userId") String userId) { | ||||||
|  |         return userServiceImpl.findById(userId).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "No user exists with the given Id")); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @GetMapping() | ||||||
|  |     public List<User> getAllUsers() { | ||||||
|  |         return userServiceImpl.findAll().orElse(new ArrayList<>()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @DeleteMapping("/{userId}") | ||||||
|  |     public ResponseEntity<Response> deleteUser(@PathVariable("userId") String userId) { | ||||||
|  |         userServiceImpl.deleteUser(userId); | ||||||
|  |         return ResponseEntity.ok(new Response(200, "The user has been deleted successfully", System.currentTimeMillis())); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,65 @@ | |||||||
|  | package com.baeldung.httpfirewall.dao; | ||||||
|  | 
 | ||||||
|  | import com.baeldung.httpfirewall.model.User; | ||||||
|  | import org.springframework.stereotype.Repository; | ||||||
|  | 
 | ||||||
|  | import java.util.ArrayList; | ||||||
|  | import java.util.HashMap; | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.Map; | ||||||
|  | import java.util.Optional; | ||||||
|  | 
 | ||||||
|  | @Repository | ||||||
|  | public class InMemoryUserDao { | ||||||
|  |     private Map<String, User> map = new HashMap<>(); | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Persists the user. The user is store in an In-Memory store (a HashMap) | ||||||
|  |      * The default implementation is an In-Memory persistence | ||||||
|  |      * @param user The user that should be persisted | ||||||
|  |      */ | ||||||
|  | 
 | ||||||
|  |     public void save(User user) { | ||||||
|  |         map.put(user.getId(), user); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Finds the user from the in-memory data store. | ||||||
|  |      * The default implementation is an In-Memory persistence | ||||||
|  |      * | ||||||
|  |      * @param userId The ID of the user that has to be fetched | ||||||
|  |      * @return An optional of the requested user | ||||||
|  |      */ | ||||||
|  |     public Optional<User> findById(String userId) { | ||||||
|  |         return Optional.ofNullable(map.get(userId)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Finds all the users from the in-memory data store | ||||||
|  |      * The default implementation is an In-Memory persistence | ||||||
|  |      */ | ||||||
|  | 
 | ||||||
|  |     public Optional<List<User>> findAll() { | ||||||
|  |         return Optional.of(new ArrayList<>(map.values())); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Delete the user from the data store | ||||||
|  |      * The default implementation is an In-Memory persistence | ||||||
|  |      * @param userId The user that has to be deleted | ||||||
|  |      */ | ||||||
|  | 
 | ||||||
|  |     public void delete(String userId) { | ||||||
|  |         map.remove(userId); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Checks if the user exists | ||||||
|  |      * The default implementation is an In-Memory persistence | ||||||
|  |      * @param userId The user that has to be checked for | ||||||
|  |      */ | ||||||
|  | 
 | ||||||
|  |     public boolean isExists(String userId) { | ||||||
|  |         return map.containsKey(userId); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,40 @@ | |||||||
|  | package com.baeldung.httpfirewall.model; | ||||||
|  | 
 | ||||||
|  | public class Response { | ||||||
|  |     private int code; | ||||||
|  |     private String message; | ||||||
|  |     private long timestamp; | ||||||
|  | 
 | ||||||
|  |     public Response() { | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public Response(int code, String message, long timestamp) { | ||||||
|  |         this.code = code; | ||||||
|  |         this.message = message; | ||||||
|  |         this.timestamp = timestamp; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public int getCode() { | ||||||
|  |         return code; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void setCode(int code) { | ||||||
|  |         this.code = code; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public String getMessage() { | ||||||
|  |         return message; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void setMessage(String message) { | ||||||
|  |         this.message = message; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public long getTimestamp() { | ||||||
|  |         return timestamp; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void setTimestamp(long timestamp) { | ||||||
|  |         this.timestamp = timestamp; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,41 @@ | |||||||
|  | package com.baeldung.httpfirewall.model; | ||||||
|  | 
 | ||||||
|  | public class User { | ||||||
|  |     private String id; | ||||||
|  |     private String username; | ||||||
|  |     private String email; | ||||||
|  | 
 | ||||||
|  |     public User() { | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public User(String id, String username, String email) { | ||||||
|  |         this.id = id; | ||||||
|  |         this.username = username; | ||||||
|  |         this.email = email; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public String getId() { | ||||||
|  |         return id; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void setId(String id) { | ||||||
|  |         this.id = id; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public String getUsername() { | ||||||
|  |         return username; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void setUsername(String username) { | ||||||
|  |         this.username = username; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public String getEmail() { | ||||||
|  |         return email; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void setEmail(String email) { | ||||||
|  |         this.email = email; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -0,0 +1,51 @@ | |||||||
|  | package com.baeldung.httpfirewall.service; | ||||||
|  | 
 | ||||||
|  | import com.baeldung.httpfirewall.dao.InMemoryUserDao; | ||||||
|  | import com.baeldung.httpfirewall.model.User; | ||||||
|  | import org.springframework.stereotype.Service; | ||||||
|  | 
 | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.Optional; | ||||||
|  | 
 | ||||||
|  | @Service | ||||||
|  | public class UserServiceImpl { | ||||||
|  | 
 | ||||||
|  |     private final InMemoryUserDao inMemoryUserDao; | ||||||
|  | 
 | ||||||
|  |     public UserServiceImpl(InMemoryUserDao inMemoryUserDao) { | ||||||
|  |         this.inMemoryUserDao = inMemoryUserDao; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Creates a user. Checks if the user already exists and then persists the user | ||||||
|  |      * @param user The user that is to be persisted into the store | ||||||
|  |      */ | ||||||
|  |     public void saveUser(User user) { | ||||||
|  |         inMemoryUserDao.save(user); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get a user. Returns a user | ||||||
|  |      * | ||||||
|  |      * @param userId The user that has to be fetched form the repository | ||||||
|  |      */ | ||||||
|  |     public Optional<User> findById(String userId) { | ||||||
|  |         return inMemoryUserDao.findById(userId); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Fetch all the users in the store | ||||||
|  |      * @return A list of all the users | ||||||
|  |      */ | ||||||
|  |     public Optional<List<User>> findAll() { | ||||||
|  |         return inMemoryUserDao.findAll(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Delete the user with a given id | ||||||
|  |      * @param userId The identifier of the user | ||||||
|  |      */ | ||||||
|  |     public void deleteUser(String userId) { | ||||||
|  |         inMemoryUserDao.delete(userId); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,2 @@ | |||||||
|  | spring.security.user.name=user | ||||||
|  | spring.security.user.password=password | ||||||
| @ -0,0 +1,114 @@ | |||||||
|  | package com.baeldung.httpfirewall.api; | ||||||
|  | 
 | ||||||
|  | import com.baeldung.httpfirewall.model.User; | ||||||
|  | 
 | ||||||
|  | import com.baeldung.httpfirewall.service.UserServiceImpl; | ||||||
|  | import com.baeldung.httpfirewall.utility.UserTestUtility; | ||||||
|  | import com.fasterxml.jackson.databind.ObjectMapper; | ||||||
|  | import org.junit.jupiter.api.BeforeEach; | ||||||
|  | import org.junit.jupiter.api.DisplayName; | ||||||
|  | import org.junit.jupiter.api.Test; | ||||||
|  | import org.springframework.beans.factory.annotation.Autowired; | ||||||
|  | import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; | ||||||
|  | import org.springframework.boot.test.context.SpringBootTest; | ||||||
|  | import org.springframework.http.HttpStatus; | ||||||
|  | import org.springframework.security.test.context.support.WithMockUser; | ||||||
|  | import org.springframework.test.web.servlet.MockMvc; | ||||||
|  | import org.springframework.test.web.servlet.MvcResult; | ||||||
|  | 
 | ||||||
|  | import java.util.List; | ||||||
|  | 
 | ||||||
|  | import static org.junit.jupiter.api.Assertions.assertEquals; | ||||||
|  | import static org.junit.jupiter.api.Assertions.assertNotNull; | ||||||
|  | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; | ||||||
|  | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; | ||||||
|  | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; | ||||||
|  | import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; | ||||||
|  | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; | ||||||
|  | 
 | ||||||
|  | @SpringBootTest | ||||||
|  | @AutoConfigureMockMvc | ||||||
|  | @DisplayName("User API Live Tests") | ||||||
|  | class UserApiLiveTest { | ||||||
|  | 
 | ||||||
|  |     private final String userId = "1"; | ||||||
|  | 
 | ||||||
|  |     @Autowired | ||||||
|  |     private MockMvc mockMvc; | ||||||
|  |     @Autowired | ||||||
|  |     private ObjectMapper objectMapper; | ||||||
|  | 
 | ||||||
|  |     @BeforeEach | ||||||
|  |     void setup() throws Exception { | ||||||
|  |         //@formatter:off | ||||||
|  |         mockMvc | ||||||
|  |           .perform(post("/api/v1/users") | ||||||
|  |             .content(objectMapper.writeValueAsString(UserTestUtility.createUserWithId(userId))) | ||||||
|  |             .contentType("application/json")); | ||||||
|  |         //@formatter:on | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     @WithMockUser | ||||||
|  |     @DisplayName("LiveTest User Creation") | ||||||
|  |     void givenCredentials_whenHttpPost_thenReturn201() throws Exception { | ||||||
|  |         // @formatter:off | ||||||
|  |         MvcResult result = mockMvc | ||||||
|  |           .perform(post("/api/v1/users") | ||||||
|  |             .content(objectMapper.writeValueAsString(UserTestUtility.createUserWithId("200"))) | ||||||
|  |             .contentType("application/json")) | ||||||
|  |           .andDo(print()) | ||||||
|  |           .andExpect(header().exists("Location")).andReturn(); | ||||||
|  | 
 | ||||||
|  |         assertEquals(HttpStatus.CREATED.value(), result.getResponse().getStatus()); | ||||||
|  |         // @formatter:on | ||||||
|  |         mockMvc.perform(delete("/api/v1/users/" + 200).contentType("application/json")); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     @WithMockUser | ||||||
|  |     @DisplayName("LiveTest Get User") | ||||||
|  |     void givenCredentials_whenHttpGetById_thenReturnUser() throws Exception { | ||||||
|  |         // @formatter:off | ||||||
|  |         MvcResult result=mockMvc | ||||||
|  |           .perform(get("/api/v1/users/"+userId) | ||||||
|  |             .contentType("application/json")).andReturn(); | ||||||
|  |         // @formatter:on | ||||||
|  |         assertEquals(HttpStatus.OK.value(), result.getResponse().getStatus()); | ||||||
|  |         assertNotNull(result.getResponse()); | ||||||
|  |         assertEquals(userId, objectMapper.readValue(result.getResponse().getContentAsString(), User.class).getId()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     @WithMockUser | ||||||
|  |     @DisplayName("LiveTest Get All Users") | ||||||
|  |     void givenCredentials_whenHttpGet_thenReturnAllUsers() throws Exception { | ||||||
|  |         // @formatter:off | ||||||
|  |         MvcResult result=mockMvc | ||||||
|  |           .perform(get("/api/v1/users/") | ||||||
|  |             .contentType("application/json")).andReturn(); | ||||||
|  |         // @formatter:on | ||||||
|  |         assertEquals(HttpStatus.OK.value(), result.getResponse().getStatus()); | ||||||
|  |         assertNotNull(result.getResponse()); | ||||||
|  |         assertNotNull(result.getResponse().getContentAsString()); | ||||||
|  | 
 | ||||||
|  |         List<User> users = objectMapper.readValue(result.getResponse().getContentAsString(), objectMapper.getTypeFactory().constructCollectionType(List.class, User.class)); | ||||||
|  | 
 | ||||||
|  |         assertEquals(1, users.size()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     @WithMockUser | ||||||
|  |     @DisplayName("LiveTest Delete User") | ||||||
|  |     void givenCredentials_whenHttpDelete_thenDeleteUser() throws Exception { | ||||||
|  |         // @formatter:off | ||||||
|  |         MvcResult result=mockMvc | ||||||
|  |           .perform(delete("/api/v1/users/"+userId) | ||||||
|  |             .contentType("application/json")).andReturn(); | ||||||
|  |         // @formatter:on | ||||||
|  |         assertEquals(HttpStatus.OK.value(), result.getResponse().getStatus()); | ||||||
|  |         assertNotNull(result.getResponse()); | ||||||
|  |         assertNotNull(result.getResponse().getContentAsString()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -0,0 +1,161 @@ | |||||||
|  | package com.baeldung.httpfirewall.api; | ||||||
|  | 
 | ||||||
|  | import com.baeldung.httpfirewall.model.User; | ||||||
|  | import com.baeldung.httpfirewall.service.UserServiceImpl; | ||||||
|  | import com.baeldung.httpfirewall.utility.UserTestUtility; | ||||||
|  | import com.fasterxml.jackson.databind.ObjectMapper; | ||||||
|  | import org.junit.jupiter.api.DisplayName; | ||||||
|  | import org.junit.jupiter.api.Test; | ||||||
|  | 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.HttpStatus; | ||||||
|  | import org.springframework.security.test.context.support.WithMockUser; | ||||||
|  | import org.springframework.test.web.servlet.MockMvc; | ||||||
|  | import org.springframework.test.web.servlet.MvcResult; | ||||||
|  | 
 | ||||||
|  | import static org.junit.jupiter.api.Assertions.assertEquals; | ||||||
|  | import static org.junit.jupiter.api.Assertions.assertNotNull; | ||||||
|  | import static org.junit.jupiter.api.Assertions.assertTrue; | ||||||
|  | import static org.mockito.Mockito.doNothing; | ||||||
|  | import static org.mockito.Mockito.when; | ||||||
|  | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; | ||||||
|  | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; | ||||||
|  | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; | ||||||
|  | import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; | ||||||
|  | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; | ||||||
|  | 
 | ||||||
|  | @WebMvcTest | ||||||
|  | @AutoConfigureMockMvc | ||||||
|  | @DisplayName("User API Unit Tests") | ||||||
|  | class UserApiUnitTest { | ||||||
|  | 
 | ||||||
|  |     @Autowired | ||||||
|  |     private MockMvc mockMvc; | ||||||
|  | 
 | ||||||
|  |     @MockBean | ||||||
|  |     private UserServiceImpl userService; | ||||||
|  | 
 | ||||||
|  |     @Autowired | ||||||
|  |     private ObjectMapper objectMapper; | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     @DisplayName("Test to Check Authentication") | ||||||
|  |     void whenNoAuthentication_thenThrow401() throws Exception { | ||||||
|  |         // @formatter:off | ||||||
|  |         MvcResult result = mockMvc | ||||||
|  |           .perform(post("/api/v1/users") | ||||||
|  |             .content(objectMapper.writeValueAsString(UserTestUtility.createUser())) | ||||||
|  |             .contentType("application/json")) | ||||||
|  |           .andReturn(); | ||||||
|  |         assertEquals(HttpStatus.UNAUTHORIZED.value(), result.getResponse().getStatus()); | ||||||
|  |         // @formatter:off | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     @WithMockUser | ||||||
|  |     @DisplayName("Test Malicious URL") | ||||||
|  |     void givenCredentials_whenMaliciousUrl_thenThrowRequestRejectedException() throws Exception { | ||||||
|  |         // @formatter:off | ||||||
|  |         MvcResult result = mockMvc | ||||||
|  |           .perform(post("/api/v1\\users") | ||||||
|  |             .content(objectMapper.writeValueAsString(UserTestUtility.createUser())) | ||||||
|  |             .contentType("application/json")) | ||||||
|  |           .andDo(print()) | ||||||
|  |           .andReturn(); | ||||||
|  |         assertEquals(HttpStatus.BAD_REQUEST.value(), result.getResponse().getStatus()); | ||||||
|  |         // @formatter:on | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     @WithMockUser | ||||||
|  |     @DisplayName("Test User Create") | ||||||
|  |     void givenCredentials_whenHttpPost_thenReturn201() throws Exception { | ||||||
|  |         // @formatter:off | ||||||
|  |         doNothing().when(userService).saveUser(new User()); | ||||||
|  | 
 | ||||||
|  |         MvcResult result=mockMvc | ||||||
|  |           .perform(post("/api/v1/users") | ||||||
|  |             .content(objectMapper.writeValueAsString(UserTestUtility.createUser())) | ||||||
|  |             .contentType("application/json")) | ||||||
|  |           .andDo(print()) | ||||||
|  |           .andExpect(header().exists("Location")).andReturn(); | ||||||
|  |         assertEquals(HttpStatus.CREATED.value(), result.getResponse().getStatus()); | ||||||
|  |         // @formatter:on | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     @WithMockUser | ||||||
|  |     @DisplayName("Test User Create Without ID") | ||||||
|  |     void givenCredentials_whenHttpPostWithId_thenReturn201() throws Exception { | ||||||
|  |         // @formatter:off | ||||||
|  |        doNothing().when(userService).saveUser(new User()); | ||||||
|  | 
 | ||||||
|  |         MvcResult result = mockMvc | ||||||
|  |           .perform(post("/api/v1/users") | ||||||
|  |             .content(objectMapper.writeValueAsString(UserTestUtility.createUserWithoutId())) | ||||||
|  |             .contentType("application/json")) | ||||||
|  |           .andDo(print()) | ||||||
|  |           .andExpect(header().exists("Location")).andReturn(); | ||||||
|  |         assertEquals(HttpStatus.CREATED.value(), result.getResponse().getStatus()); | ||||||
|  |         // @formatter:on | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     @WithMockUser | ||||||
|  |     @DisplayName("Test Get User") | ||||||
|  |     void givenCredentials_whenHttpGetWithId_thenReturnUser() throws Exception { | ||||||
|  |         String userId = "1"; | ||||||
|  |         // @formatter:off | ||||||
|  |         when(userService.findById("1")).thenReturn(UserTestUtility.createUserWithId(userId)); | ||||||
|  | 
 | ||||||
|  |         MvcResult result = mockMvc | ||||||
|  |           .perform(get("/api/v1/users/"+userId) | ||||||
|  |             .accept("application/json")) | ||||||
|  |           .andDo(print()) | ||||||
|  |           .andReturn(); | ||||||
|  |         assertEquals(HttpStatus.OK.value(), result.getResponse().getStatus()); | ||||||
|  |         assertNotNull(result.getResponse()); | ||||||
|  |         assertEquals("jhondoe",objectMapper.readValue(result.getResponse().getContentAsString(),  User.class).getUsername()); | ||||||
|  | 
 | ||||||
|  |         // @formatter:on | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     @WithMockUser | ||||||
|  |     @DisplayName("Test Get All Users") | ||||||
|  |     void givenCredentials_whenHttpGetWithoutId_thenReturnAllUsers() throws Exception { | ||||||
|  |         // @formatter:off | ||||||
|  |         when(userService.findAll()).thenReturn(UserTestUtility.createUsers()); | ||||||
|  | 
 | ||||||
|  |         MvcResult result = mockMvc | ||||||
|  |           .perform(get("/api/v1/users/") | ||||||
|  |             .accept("application/json")) | ||||||
|  |           .andDo(print()) | ||||||
|  |           .andReturn(); | ||||||
|  |         assertEquals(HttpStatus.OK.value(), result.getResponse().getStatus()); | ||||||
|  |         assertNotNull(result.getResponse()); | ||||||
|  |         assertTrue(result.getResponse().getContentAsString().contains("jane.doe")); | ||||||
|  |         // @formatter:on | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     @WithMockUser | ||||||
|  |     @DisplayName("Test Delete a User") | ||||||
|  |     void givenCredentials_whenHttpDelete_thenDeleteUser() throws Exception { | ||||||
|  |         String userId = "1"; | ||||||
|  |         doNothing().when(userService).deleteUser(userId); | ||||||
|  |         // @formatter:off | ||||||
|  |         MvcResult result = mockMvc | ||||||
|  |           .perform(delete("/api/v1/users/"+userId) | ||||||
|  |             .accept("application/json")) | ||||||
|  |           .andDo(print()) | ||||||
|  |           .andReturn(); | ||||||
|  |         assertEquals(HttpStatus.OK.value(), result.getResponse().getStatus()); | ||||||
|  |         assertNotNull(result.getResponse()); | ||||||
|  |         assertTrue(result.getResponse().getContentAsString().contains("The user has been deleted successfully")); | ||||||
|  |         // @formatter:on | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -0,0 +1,92 @@ | |||||||
|  | package com.baeldung.httpfirewall.service; | ||||||
|  | 
 | ||||||
|  | import com.baeldung.httpfirewall.dao.InMemoryUserDao; | ||||||
|  | 
 | ||||||
|  | import com.baeldung.httpfirewall.model.User; | ||||||
|  | import com.baeldung.httpfirewall.utility.UserTestUtility; | ||||||
|  | import org.junit.jupiter.api.BeforeEach; | ||||||
|  | import org.junit.jupiter.api.DisplayName; | ||||||
|  | import org.junit.jupiter.api.Test; | ||||||
|  | import org.mockito.InjectMocks; | ||||||
|  | import org.mockito.Mock; | ||||||
|  | import org.mockito.MockitoAnnotations; | ||||||
|  | 
 | ||||||
|  | import java.util.ArrayList; | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.Optional; | ||||||
|  | 
 | ||||||
|  | import static org.junit.jupiter.api.Assertions.assertEquals; | ||||||
|  | import static org.junit.jupiter.api.Assertions.assertNotNull; | ||||||
|  | 
 | ||||||
|  | import static org.junit.jupiter.api.Assertions.assertThrows; | ||||||
|  | 
 | ||||||
|  | import static org.mockito.Mockito.doNothing; | ||||||
|  | import static org.mockito.Mockito.times; | ||||||
|  | import static org.mockito.Mockito.verify; | ||||||
|  | import static org.mockito.Mockito.when; | ||||||
|  | 
 | ||||||
|  | @DisplayName("UserService Unit Tests") | ||||||
|  | class UserServiceUnitTest { | ||||||
|  | 
 | ||||||
|  |     @InjectMocks | ||||||
|  |     private UserServiceImpl userService; | ||||||
|  | 
 | ||||||
|  |     @Mock | ||||||
|  |     private InMemoryUserDao userDao; | ||||||
|  | 
 | ||||||
|  |     @BeforeEach | ||||||
|  |     void setup() { | ||||||
|  |         MockitoAnnotations.openMocks(this); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     @DisplayName("Check Create User") | ||||||
|  |     void whenCalledCreateUser_thenVerify() { | ||||||
|  |         User user = UserTestUtility.createUser(); | ||||||
|  |         doNothing().when(userDao).save(user); | ||||||
|  | 
 | ||||||
|  |         userService.saveUser(user); | ||||||
|  |         verify(userDao, times(1)).save(user); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     @DisplayName("Check Get User") | ||||||
|  |     void givenUserId_whenCalledFindById_thenReturnUser() { | ||||||
|  |         User user = UserTestUtility.createUserWithId("1").orElse(new User("1", "jhondoe", "jhon.doe@gmail.com")); | ||||||
|  | 
 | ||||||
|  |         when(userDao.findById(user.getId())).thenReturn(Optional.of(user)); | ||||||
|  | 
 | ||||||
|  |         User actualUser = userService.findById("1").get(); | ||||||
|  | 
 | ||||||
|  |         assertNotNull(actualUser); | ||||||
|  |         assertEquals("jhondoe", actualUser.getUsername()); | ||||||
|  |         verify(userDao, times(1)).findById(user.getId()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     @DisplayName("Check Get All Users") | ||||||
|  |     void whenCalledFindAll_thenReturnAllUsers() { | ||||||
|  |         List<User> users = UserTestUtility.createUsers().orElse(new ArrayList<>()); | ||||||
|  | 
 | ||||||
|  |         when(userDao.findAll()).thenReturn(Optional.of(users)); | ||||||
|  | 
 | ||||||
|  |         Optional<List<User>> actualUsers = userService.findAll(); | ||||||
|  | 
 | ||||||
|  |         assertNotNull(actualUsers); | ||||||
|  |         assertEquals(2, users.size()); | ||||||
|  |         verify(userDao, times(1)).findAll(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     @DisplayName("Check Delete Users") | ||||||
|  |     void givenId_whenCalledDeleteUser_thenDeleteUser() { | ||||||
|  |         User user = UserTestUtility.createUserWithId("1").orElse(new User()); | ||||||
|  | 
 | ||||||
|  |         doNothing().when(userDao).delete(user.getId()); | ||||||
|  |         userService.deleteUser(user.getId()); | ||||||
|  |         verify(userDao, times(1)).delete(user.getId()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -0,0 +1,33 @@ | |||||||
|  | package com.baeldung.httpfirewall.utility; | ||||||
|  | 
 | ||||||
|  | import com.baeldung.httpfirewall.model.User; | ||||||
|  | 
 | ||||||
|  | import java.util.Arrays; | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.Optional; | ||||||
|  | import java.util.UUID; | ||||||
|  | 
 | ||||||
|  | public class UserTestUtility { | ||||||
|  |     public static User createUser() { | ||||||
|  |         return new User(UUID.randomUUID().toString(),"jhondoe", "jhondoe@gmail.com"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public static User createUserWithoutId() { | ||||||
|  |         return new User("","jhondoe", "jhondoe@gmail.com"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public static Optional<User> createUserWithId(String id) { | ||||||
|  |         // @formatter:off | ||||||
|  |         return Optional.of(new User(id, "jhondoe", "jhon.doe@gmail.com")); | ||||||
|  |         // @formatter:on | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public static Optional<List<User>> createUsers() { | ||||||
|  |         // @formatter:off | ||||||
|  |         return Optional.of(Arrays.asList( | ||||||
|  |           new User(UUID.randomUUID().toString(), "jhondoe","jhon.doe@gmail.com" ), | ||||||
|  |           new User(UUID.randomUUID().toString(), "janedoe","jane.doe@gmail.com" )) | ||||||
|  |         ); | ||||||
|  |         // @formatter:on | ||||||
|  |     } | ||||||
|  | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user