Spring MVC test controller with spring security - java

I am trying to write the test case for test controller, here is the code for the controller
#Controller
#RequestMapping("/")
#SessionAttributes({"roles", "departments"})
public class AppController {
#Autowired
UserService userService;
#Autowired
RoleService roleService;
#Autowired
DepartmentService departmentService;
#Autowired
MessageSource messageSource;
#Autowired
PersistentTokenBasedRememberMeServices persistentTokenBasedRememberMeServices;
#Autowired
AuthenticationTrustResolver authenticationTrustResolver;
static final Logger logger = LoggerFactory.getLogger(AppController.class);
/**
* This method will list all existing users.
*/
#RequestMapping(value = { "/", "/list" }, method = RequestMethod.GET)
public String listUsers(ModelMap model) {
List<User> users = userService.findAllUsers();
model.addAttribute("users", users);
model.addAttribute("loggedinuser", getPrincipal());
return "userslist";
}
/**
* This method returns the principal[user-name] of logged-in user.
*/
private String getPrincipal(){
String userName = null;
Object principal = getCurrentUser();
if (principal instanceof UserDetails) {
userName = ((UserDetails)principal).getUsername();
} else {
userName = principal.toString();
}
return userName;
}
private Object getCurrentUser(){
return SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}
//The rest part of the controller}
I using TestNG based on the this tutorial: http://websystique.com/springmvc/spring-4-mvc-and-hibernate4-integration-testing-example-using-annotations/, and currently I have following in my test cases:
//all the import file
public class AppControllerTest {
#Mock
UserService userService;
#Mock
MessageSource message;
#InjectMocks
AppController appController;
#Spy
List<User> users = new ArrayList<User>();
#Spy
ModelMap model;
#Mock
BindingResult result;
#BeforeClass
public void setUp(){
MockitoAnnotations.initMocks(this);
users = getUsers();
}
private List<User> getUsers() {
// TODO Auto-generated method stub
User u1 = new User();
u1.setId(1);
u1.setFirstName("Admin");
u1.setLastName("Admin");
u1.setUsername("admin");
u1.setEmail("admin#akb.co.jp");
u1.setDateOfBirth(new LocalDate());
u1.setPassword("admin");
Department admin = new Department();
admin.setId(1);
admin.setName("Admin");
admin.setDescription("Admin");
u1.setDepartment(admin);
Role adminRole = new Role();
adminRole.setId(1);
adminRole.setRoleName("ADMIN");
Set<Role> roles = new HashSet<>();
roles.add(adminRole);
u1.setRoles(roles);
User u2 = new User();
u2.setId(1);
u2.setFirstName("Alice");
u2.setLastName("Lin");
u2.setUsername("alice.lin");
u2.setEmail("alice.lin#akb.co.jp");
u2.setDateOfBirth(new LocalDate());
u2.setPassword("Alice0102");
u2.setDepartment(admin);
u2.setRoles(roles);
users.add(u1);
users.add(u2);
return users;
}
#Test
public void listUsers(){
when(userService.findAllUsers()).thenReturn(users);
Assert.assertEquals(appController.listUsers(model), "userslist");
Assert.assertEquals(model.get("users"), users);
verify(userService, atLeastOnce()).findAllUsers();
}
}
Now the question is, if I didn't comment this line model.addAttribute("loggedinuser", getPrincipal());
in my controller class, when I run maven test, it will throw null pointer exception, that is obvious, since in my test cases I didn't login to the application. What can I do so I can make the test passed include this line?

You should refactor your code so that the getCurrentUser() calls live in a separate class. You should keep those separate anyway because most likely other controllers will need to make the same calls. But for this context, you need to refactor because you cannot mock private method calls (at least not using Mockito).
Once the user related calls are in a separate class, you can mock it just as you have done the other services above, using #Mock annotation.

Related

while writing Junit test cases for #post method, i am getting null pointer exception at userService.addUser() mehod. could anyone help here

public class UserService {
#Autowired
private UserRepository userRepository;
#Autowired
private ConverterService converterService;
public User addUser(UserDto userdto) {
User convertedUser = converterService.convertToEntity(userdto);
convertedUser.setUserId(userdto.getUserId());
convertedUser.setUserName(userdto.getUserName());
User savedUser = userRepository.save(convertedUser);
return savedUser;
}
}
//while debugging userRepository.save(convertedUser) method, it always returning null.
Below is my UserServiceTest.java class
#RunWith(SpringRunner.class)
#SpringBootTest
public class UserServiceTest {
#Autowired
private UserService userService;
#Mock
private ConverterService converterService;
#MockBean
private UserRepository userRepository;
#Test
public void addUserTest() {
UserDto userDto = new UserDto();
userDto.setUserId("123");
userDto.setUserName("AB");
User user = new User("123","ABC");
Mockito.when(converterService.convertToEntity(new UserDto())).thenReturn(user);
Mockito.when(userRepository.save(user)).thenReturn(user);
User user1 = userService.addUser(userDto);
Assert.assertEquals(user,userService.addUser(userDto));
}
}
userService.addUser(userDto) this method is always return by null from service class. because of below condition is failing : Assert.assertEquals(user,userService.addUser(userDto));
you shouldn't pass null value to the assertEquals method. But you can do like this;
Assert.assertEquals(Objects.equals(user,userService.addUser(userDto)),true);

rawPassword cannot be null exception

I have the following problem:
I want to test if a user registers successfully. I'm not doing this over a UI but via Postman. The problem is that I get an exception when I use the POST Command. I have already checked all annotations.
Postman Screenshot
Exception:
java.lang.IllegalArgumentException: rawPassword cannot be null
at org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder.encode(BCryptPasswordEncoder.java:103) ~[spring-security-core-5.3.3.RELEASE.jar:5.3.3.RELEASE]
at com.example.application.backend.data.service.UserService.singUpUser(UserService.java:41) ~[classes/:na]
at com.example.application.backend.data.registration.RegistrationService.register(RegistrationService.java:22) ~[classes/:na]
at com.example.application.backend.data.registration.RegistrationController.register(RegistrationController.java:18)
This is my UserService Class:
private final static String USER_NOT_FOUND_MSG = "user with email %s not found";
#Autowired
private final UserRepository userRepository;
#Autowired
private final BCryptPasswordEncoder bCryptPasswordEncoder;
public UserService(
UserRepository userRepository,
BCryptPasswordEncoder bCryptPasswordEncoder) {
this.userRepository = userRepository;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
#Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
return userRepository.findByEmail(email)
.orElseThrow(() ->
new UsernameNotFoundException(String.format(USER_NOT_FOUND_MSG, email)));
}
public String singUpUser(User user) {
boolean userExists = userRepository.findByEmail(user.getEmail()).isPresent();
if(userExists){
throw new IllegalStateException("email already taken");
}
String encodedPassword = bCryptPasswordEncoder.encode(user.getPassword());
user.setPassword(encodedPassword);
userRepository.save(user);
return "it works";
}
Registration Service Class:
#Autowired
private final UserService userService;
private final EmailValidator emailValidator;
public String register(RegistrationRequest request) {
boolean isValidEmail = emailValidator.test(request.getEmail());
if (!isValidEmail) {
throw new IllegalStateException("email not valid");
}
return userService.singUpUser(
new User(
request.getFirstName(),
request.getLastName(),
request.getEmail(),
request.getPassword(),
UserRole.USER
)
);
}
public RegistrationService(UserService userService, EmailValidator emailValidator) {
this.userService = userService;
this.emailValidator = emailValidator;
}
Registration Controller Class:
#Autowired
private RegistrationService registrationService;
#PostMapping
public String register(#RequestBody RegistrationRequest request) {
return registrationService.register(request);
}
This error occurs when the password string that is fed to the bCryptEncoder is empty or null.
In your UserService.java class,
String encodedPassword = bCryptPasswordEncoder.encode(user.getPassword());
user.getPassword() is null, which means in RegistrationService.java class,
request.getPassword()
is fetching a null value.
Please check if you are getting the correct parameter name for password from the request object. If possible, add some logger statements in your code and debug.
GitHub PR
It is working. The problem was that the #Column annotations were set to nullable false.
Expanding #Sidharth's answer.
For anyone experiencing this problem check your password getter at AppUser.java class. It is probably returning null.
Change
public String getPassword(){return null;}
To
public String getPassword(){return password;}
please pay attention when we import the module request body
make sure the import is org.springframework.web.bind.annotation.RequestBody;

Spring Boot registration form testing - NullPointerException

I learning Spring Boot web application with .jsp, and I'm struggling a lot with the testing concepts. From the SO and YT guides I implemented the Mockito thing, but honestly I do not clearly undesrtand how does it work.
I have a Registration form with 4 fields for name, lastname, email and password. This POST request is handled by the registerAction method in RegisterController. In this method I have two self-written validators for email and password. The tests should handle the cases when User data are given properly and if the errors are sent when inputs are not correct.
I tried to write tests for the controller but I'm constantly getting an exception NullPointerExpection. Looking into the debugger, the User object sent from the testing class has null attributes, which probably is the reason the exceptions.
Testing class:
#SpringBootTest
#AutoConfigureMockMvc
class RegisterControllerTest {
#Autowired
private WebApplicationContext wac;
#MockBean
private UserService userService;
#Autowired
private Gson gson;
#Autowired
private MockMvc mockMvc;
#BeforeEach
void setUp() {
initMocks(this);
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac)
.apply(springSecurity()).build();
}
#Test
void show_register_action() throws Exception {
User user = prepareUserEmpty();
this.mockMvc.perform(post("/adduser")
.contentType(MediaType.APPLICATION_JSON)
.content(gson.toJson(user)))
.andDo(print())
.andExpect(status().isOk());
}
private User prepareUserEmpty(){
User user = new User();
user.setEmail("");
user.setPassword("");
user.setName("");
user.setLastName("");
return user;
}
}
RegisterController:
#Controller public class RegisterController {
#Autowired
private UserService userService;
#Autowired
private EmailSender emailSender;
#Autowired
MessageSource messageSource; // Allows to obtain messages from message.properties to Java code
#POST
#RequestMapping(value = "/adduser")
public String registerAction(User user, BindingResult result, Model model, Locale locale){ // BindingResult for validation, Locale for messageSource
String returnPage = "register";
User userExist = userService.findUserByEmail(user.getEmail());
new UserRegisterValidator().validate(user, result);
new UserRegisterValidator().validateEmailExist(userExist, result);
if (!(result.hasErrors())){
userService.saveUser(user);
model.addAttribute("message", messageSource.getMessage("user.register.success.email", null, locale));
returnPage = "index";
}
return returnPage;
} }
Validators:
public class UserRegisterValidator implements Validator {
#Override
public boolean supports(Class <?> cls){
return User.class.equals(cls);
}
#Override
public void validate(Object obj, Errors errors){
User u = (User) obj;
ValidationUtils.rejectIfEmpty(errors, "name", "error.userName.empty");
ValidationUtils.rejectIfEmpty(errors, "lastName", "error.userLastName.empty");
ValidationUtils.rejectIfEmpty(errors, "email", "error.userEmail.empty");
ValidationUtils.rejectIfEmpty(errors, "password", "error.userPassword.empty");
if (!u.getEmail().equals(null)){
boolean isMatch = AppdemoUtils.checkEmailOrPassword(AppdemoConstants.EMAIL_PATTERN, u.getEmail());
if (!isMatch)
errors.rejectValue("email", "error.userEmailIsNotMatch");
}
if (!u.getPassword().equals(null)){
boolean isMatch = AppdemoUtils.checkEmailOrPassword(AppdemoConstants.PASSWORD_PATTERN, u.getPassword());
if (!isMatch)
errors.rejectValue("password", "error.userPasswordIsNotMatch");
}
}
public void validateEmailExist(User user, Errors errors){
if (user != null)
errors.rejectValue("email", "error.userEmailExist");
}
}

How to handle user information after login succeed in Spring Security

I want to use user-information after login succeed. I thought about storing it into session attribute. or using #scope('session) annotation. but I haven't found the best way of doing it. so I just stored it into model-attribute.
#Controller
#RequestMapping(value = "/user")
public class UserController {
#Autowired
private UserService userService;
#Autowired
private UserProfileService userProfileService;
#ModelAttribute("user")
public User getUserModel () {
return userService.findByEmail(SecurityContextHolder.getContext().getAuthentication().getName());
}
#ModelAttribute("userProfile")
public UserProfile getUserProfile() {
return userProfileService.findByUser(userService.findByEmail(SecurityContextHolder.getContext().getAuthentication().getName()));
}
#GetMapping("/")
public String userIndex() {
logger.info("UserIndex");
return "userPage";
}
As you can see, SecurityContextHolder.getContext().getAuthentication().getName() --> this method repeated. every time user make HTTP request, is this good practice? or any better way of store user-infomation in application?
I would go with this.
#RequestMapping(value = "/username", method = RequestMethod.GET)
#ResponseBody
public String currentUserName(Authentication authentication) {
return authentication.getName();
}

Cant put users ID as FK for added database entry (bind added entry to currently loggedin user) - Hibernate, Spring, Spring Security -

I am working on a small library management system where users log in and add books from their collection. I am using Spring Boot with Spring Security, MySql and Hibernate. Users are able to login and when they are logged in I can take their info via ContexHolder and I can add books to database and get them all and display them in front-end, but now I want to bind every added book with currently logged in user so I can display only books this user has added, not all books from db. I am trying to do it like this:
My BookController.java:
#RestController
#RequestMapping("/book")
public class BookController {
#Resource
BookDAO bookDao;
#RequestMapping(value = "/save", method = RequestMethod.POST)
public HashMap<String, String> addBook(#RequestBody Book book) {
book.setUser(new Security().getCurrentUser());
bookDao.save(book);
return Response.setSuccess("Book saved successfuly.");
}
My Security.java:
public class Security {
private UserDAO userDao;
#RequestMapping(value = "/getCurentUser", method = RequestMethod.GET)
public User getCurrentUser() {
return userDao.findByEmail(SecurityContextHolder.getContext().getAuthentication().getName());
}
}
But, for some reason, in this case, method getCurrentUser returns null and I get NullPointerException. But when I use the same method from UserController.java it returns exactly what I expect i to return. This is tested at the same run (first I called a method from Securty.java and it returned null, then I called method from UserControll.java and I got currently logged in user (me) ).
Here is my UserController.java (only getCurrentUser and loginUser methods):
#RestController
#RequestMapping("/user")
public class UserController {
#Resource
private UserDAO userDao;
#Autowired
AuthenticationManager authenticationManager;
#Autowired
BCryptPasswordEncoder encoder;
#RequestMapping(value = "/getCurentUser", method = RequestMethod.GET)
public User getCurrentUser() {
return userDao.findByEmail(SecurityContextHolder.getContext().getAuthentication().getName());
}
...
#RequestMapping(value = "/login", method = {RequestMethod.POST, RequestMethod.GET})
public HashMap<String, String> loginUser(#RequestBody final User user) {
final User foundUser = userDao.findByEmail(user.getEmail());
if (foundUser == null || !encoder.matches(user.getCurentPassword(), foundUser.getCurentPassword())) {
return Response.setError("Bad credentials");
} else {
final UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(user.getEmail(), user.getCurentPassword(), AuthorityUtils.createAuthorityList(foundUser.getRole().name()));
final Authentication authentication = authenticationManager.authenticate(authRequest);
SecurityContextHolder.getContext().setAuthentication(authentication);
return Response.setSuccess(foundUser.getRole().name());
}
}
Oh, and my Book.Java bean:
....
#ManyToOne
#JoinColumn(name = "user_id")
private User user;
....
Finally after long time of trying i have managed to solve this problem. As it turned out - calling getCurrentUser method from separate Security class returned anonymous user even thou user with ROLE_USER was logged in. Adding #Autowired annots to UserDAO userDao or #Component or #Service annots to whole class also did not gave any results.
Calling:
userDao.findByEmail(SecurityContextHolder.getContext().getAuthentication().getName())
from BookController with UserDAO userDao eith #Resource annot in BookController also returned anonymous user. Only when I added #Autowired to UserDAO userDao i managed to get currently logged in user and bind book to him. Here is the code:
#RestController
#RequestMapping("/book")
public class BookController {
#Resource
BookDAO bookDao;
#Autowired
UserDAO userDao;
#PreAuthorize("hasRole('ROLE_USER')")
#RequestMapping(value = "/save", method = {RequestMethod.POST, RequestMethod.GET})
public HashMap<String, String> addBook(#RequestBody Book book) {
book.setUser(userDao.findByEmail(SecurityContextHolder.getContext().getAuthentication().getName()));
bookDao.save(book);
return Response.setSuccess("Book saved successfuly.");
}
1- The class Security has a #Component or #Service annotation?
2- userDao field is null - you need to inject it using #Autowired to avoid NullPointerException in getCurrentUser() method.

Categories

Resources