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;
Related
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);
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");
}
}
I have jHipster Project with 2 Entities - User and Client. Client has field "user_id" which keeps user_id of that user which created a client entity.
ClientResource.java
#RestController
#RequestMapping("/api")
public class ClientResource {
private static final String ENTITY_NAME = "client";
private final Logger log = LoggerFactory.getLogger(ClientResource.class);
private final ClientService clientService;
private final UserService userService;
private final ClientQueryService clientQueryService;
public ClientResource(ClientService clientService, ClientQueryService clientQueryService, UserService userService) {
this.clientService = clientService;
this.clientQueryService = clientQueryService;
this.userService = userService;
}
....................
#PostMapping("/clients")
#Timed
public ResponseEntity<ClientDTO> createClient(#RequestBody ClientDTO clientDTO) throws URISyntaxException {
log.debug("REST request to save Client : {}", clientDTO);
if (clientDTO.getId() != null) {
throw new BadRequestAlertException("A new client cannot already have an ID", ENTITY_NAME, "idexists");
}
String login = SecurityUtils.getCurrentUserLogin().toString();
Long loggedUserId = userService.getUserWithAuthoritiesByLogin(login).get().getId();
Here i got exception:
Exception in com.mycompany.hiptest.web.rest.ClientResource.createClient() with cause = 'NULL' and exception = 'No value present'
java.util.NoSuchElementException: No value present
I guess because i haven' a initilized clientService variable.
But how clientService got value? I didn't found any calls of ClientResource constructor.
I've exhausted every SO post and blog entry I could find trying to figure out what I've done wrong. So, now I'm asking for your help. I'm building a Spring Boot app and leveraging Spring Security for user management/authentication. I think I've set everything up correctly, but during login Spring Security redirects to the login failure URL (/login?error) every time, throwing the error:
Your login attempt was not successful, try again.
Reason: Bad credentials
There is nothing telling or useful in the server logs. As part of debugging, I added a bunch of logging which confirmed that:
The user is being found in the database (via email, with usernameParameter set to email in config
Spring Security's userDetails.User creates the correct user from its new constructor (I've logged the details of the user)
I am new to the framework so it's possible I'm overlooking something, which is where I would really appreciate your help. I've included my security config and user service below (I've cleared out logging to clean it up for your reading)--let me know if any other pieces would be helpful. Thank you in advance!
SecurityConfiguration.java
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Resource(name = "userService")
private UserDetailsService userDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/test").hasRole("USER")
.antMatchers("/**").permitAll()
.and().formLogin().usernameParameter("email").defaultSuccessUrl("/register_success");
http.authorizeRequests()
.antMatchers("/resources/**").permitAll();
super.configure(http);
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder authenticationManagerBuilder)
throws Exception {
authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
// Define this bean so autowired can find and use it (fixes complaining error)
#Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
UserService.java
#Service("userService")
public class UserService implements UserDetailsService {
private static final String LOG_TAG = UserService.class.getSimpleName();
private Logger logger = LoggerFactory.getLogger(LOG_TAG);
private UserRepository userRepository;
#Autowired
SessionFactory sessionFactory;
#Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
// Must provide email address as username argument
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByEmail(username);
if (user == null) {
throw new UsernameNotFoundException("There is no user with this email address.");
}
org.springframework.security.core.userdetails.User springUser = new org.springframework.security.core.userdetails.User(
user.getEmail(), user.getPassword(), getAuthority());
return springUser;
}
public User findByEmail(String email) {
return userRepository.findByEmail(email);
}
public User findByConfirmationToken(String confirmationToken) {
return userRepository.findByConfirmationToken(confirmationToken);
}
#Transactional
public List<User> findAll() {
Criteria criteria = sessionFactory.openSession().createCriteria(User.class);
return (List<User>) criteria.list();
}
public void saveUser(User user) {
userRepository.save(user);
}
//TODO: Figure out how to use this properly
public List getAuthority() {
return Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"));
}
}
You need to use PasswordEncoder to encrypt passwords when adding new users.
like this:
#Autowired
private BCryptPasswordEncoder passwordEncoder;
public void saveUser(User user) {
user.setPassword(passwordEncoder.encode(user.getPassword()));
userRepository.save(user);
}
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.