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…
Reference in New Issue