Spring #ModelAttribute object fields are always null - java

Im trying to use ModelAttribute annotation to bind thymeleaf form and model but whatever i do it still null and i've searched for days..
Controller method
#RequestMapping("/signup")
public String register(#ModelAttribute("request") RegistrationRequest request, HttpSession session, Model model) {
if (session.getAttribute("user") != null) {
User user = (User) session.getAttribute("user");
LOGGER.info("User {} redirected to home instead of signup because he was already logged", user.getUsername());
return "redirect:/";
}
model.addAttribute("request", new RegistrationRequest());
model.addAttribute("IS_SUCCESS", service.handleRegistrationRequest(request, session));
model.addAttribute("USERNAME_FIELD_ERROR", session.getAttribute("USERNAME_FIELD_ERROR"));
model.addAttribute("EMAIL_FIELD_ERROR", session.getAttribute("EMAIL_FIELD_ERROR"));
session.removeAttribute("USERNAME_FIELD_ERROR");
session.removeAttribute("EMAIL_FIELD_ERROR");
return "signup_step1";
}
HTML part
<form th:action="#{/signup}" method="post" th:object="${request}">
<div id="username-input-wrapper" class="input-wrapper" th:classappend="${USERNAME_FIELD_ERROR != null} ? 'input-wrapper-error' : ''">
<label for="username" th:text="#{signup.label.username}"></label>
<input id="username" class="form-control" type="text" name="username" th:field="*{username}" th:placeholder="#{signup.textfield.prompt.username}">
<span class="error-message" th:text="${USERNAME_FIELD_ERROR != null} ? #{${USERNAME_FIELD_ERROR}} : ''"></span>
</div>
<div id="email-input-wrapper" class="input-wrapper" th:classappend="${EMAIL_FIELD_ERROR != null} ? 'input-wrapper-error' : ''">
<label for="email" th:text="#{signup.label.email}"></label>
<input id="email" class="form-control" type="text" name="email" th:field="*{email}" th:placeholder="#{signup.textfield.prompt.email}">
<span class="error-message" th:text="${EMAIL_FIELD_ERROR != null} ? #{${EMAIL_FIELD_ERROR}} : ''"></span>
</div>
<div class="submit-box">
<p class="conditions">
By creating an account, you agree to our Terms and Privacy Policy.
</p>
<button class="signup-btn" type="submit" th:text="#{signup.button.continue}"></button>
</div>
</form>

Related

SpringBoot/SpringSecurity logged in user can't update their details, no error being got but update not happening in database

I am trying to allow currently logged in user of my spring application update their current details but it is not persisting to the database, I am getting no errors and have tried to debug but have gotten no success.. please take a look.
Service class:
#Transactional
public User updateAccount(User userInForm){
System.out.println("Fetching user with id: " + userInForm.getId());
Optional<User> optionalUser = repo.findById(userInForm.getId());
if(!optionalUser.isPresent()){
System.out.println("User not found.");
return null;
}
User userInDB = optionalUser.get();
System.out.println("User fetched: " + userInDB);
userInDB.setFirstName(userInForm.getFirstName());
userInDB.setLastName(userInForm.getLastName());
System.out.println("Saving updated user: " + userInDB);
User savedUser = repo.save(userInDB);
System.out.println("User saved: " + savedUser);
return savedUser;
}
Controller class:
#PostMapping("/myAccount/update")
public String updateAccount(User user, RedirectAttributes redirectAttributes, Principal principal){
System.out.println("Updating user details...");
user = repo.findByEmail(principal.getName());
User updatedUser = service.updateAccount(user);
if (updatedUser == null) {
System.out.println("Error updating user details.");
} else {
redirectAttributes.addFlashAttribute("message", "Details Updated!");
return "redirect:/myAccount";
}
return "redirect:/myAccount";
}
Front end:
<h1 style="color:green">Welcome <b>[[${#request.userPrincipal.principal.fullName}]]</b></h1>
<h2 style="color:green">My Details</h2>
<div th:if="${message}" class ="alert alert-success text-center">
[[${message}]]
</div>
<form th:action="#{/myAccount/update}" th:object="${user}"
method="post" style="max-width: 600px; margin: 0 auto;">
<div class="m-3">
<div class="form-group row">
<label class="col-4 col-form-label">E-mail: </label>
<div class="col-8">
<input type="email" th:field="*{email}" class="form-control" readonly="readonly" />
</div>
</div>
<div class="form-group row">
<label class="col-4 col-form-label">Password: </label>
<div class="col-8">
<input type="password" th:field="*{password}" placeholder="Leave blank if you don't want to change!" class="form-control"
minlength="6" maxlength="10"/>
</div>
</div>
<div class="form-group row">
<label class="col-4 col-form-label">First Name: </label>
<div class="col-8">
<input type="text" th:field="*{firstName}" class="form-control"
required minlength="2" maxlength="20"/>
</div>
</div>
<div class="form-group row">
<label class="col-4 col-form-label">Last Name: </label>
<div class="col-8">
<input type="text" th:field="*{lastName}" class="form-control"
required minlength="2" maxlength="20" />
</div>
</div>
<div>
<button type="submit" class="btn btn-primary">Update Details</button>
</div>
</div>
</form>
Printed statements in console:
Updating user details...
Fetching user with id: 1
User fetched: com.example.Model.User#330603d0
Saving updated user: com.example.Model.User#330603d0
User saved: com.example.Model.User#330603d0
You are reassigning the user object received from the request to some other value. Check the below lines from the controller method
user = repo.findByEmail(principal.getName()); // this line reassigning the user object from the request to that of the one in database.
User updatedUser = service.updateAccount(user);
Because of this, user details are getting updated but with the existing data.

registration form is not submitting in spring boot and not hitting endpoints

I'm working on a registration form in Spring Boot and thymeleaf but it's not submitting to the database. I even run the debugger mood and it did not hit the endpoints for the registration. Note, that my login and other functionalities work fine. I'm following this here to create my registration
How to I get this endpoint to create a new user?
register.html
<form th:action="#{/registration}" th:object="${user}" method="post">
<p class="error-message" th:if="${#fields.hasGlobalErrors()}"
th:each="error : ${#fields.errors('global')}" th:text="${error}">Validation
error</p>
<div class="form-group"
th:classappend="${#fields.hasErrors('firstName')}? 'has-error':''">
<label for="firstName" class="control-label">First name</label> <input
id="firstName" class="form-control" th:field="*{firstName}" />
<p class="error-message"
th:each="error: ${#fields.errors('firstName')}"
th:text="${error}">Validation error</p>
</div>
<div class="form-group"
th:classappend="${#fields.hasErrors('lastName')}? 'has-error':''">
<label for="lastName" class="control-label">Last name</label> <input
id="lastName" class="form-control" th:field="*{lastName}" />
<p class="error-message"
th:each="error : ${#fields.errors('lastName')}"
th:text="${error}">Validation error</p>
</div>
<div class="form-group"
th:classappend="${#fields.hasErrors('email')}? 'has-error':''">
<label for="email" class="control-label">E-mail</label> <input
id="email" class="form-control" th:field="*{email}" />
<p class="error-message"
th:each="error : ${#fields.errors('email')}" th:text="${error}">Validation
error</p>
</div>
<div class="form-group"
th:classappend="${#fields.hasErrors('username')}? 'has-error':''">
<label for="email" class="control-label">Username</label> <input
id="email" class="form-control" th:field="*{username}" />
<p class="error-message"
th:each="error : ${#fields.errors('username')}" th:text="${error}">Validation
error</p>
</div>
<div class="form-group"
th:classappend="${#fields.hasErrors('password')}? 'has-error':''">
<label for="password" class="control-label">Password</label> <input
id="password" class="form-control" type="password"
th:field="*{password}" />
<p class="error-message"
th:each="error : ${#fields.errors('password')}"
th:text="${error}">Validation error</p>
</div>
<!--
<div class="form-group">
<button type="submit" class="btn btn-success">Register</button>
<span>Already registered? <a href="/" th:href="#{/login}">Login
here</a></span>
</div>
-->
<div class="form-group">
<button type="submit" class="btn btn-success">Register</button>
</div>
</form>
controller.java
#Controller
public class RegisterationController {
#Autowired
private UserService userService;
#GetMapping("/register")
public String getRegisterPage(ModelMap model) {
User user = new User();
model.put("user", user);
return "register";
}
#PostMapping("/registration")
public String postRegister(#RequestBody User user, BindingResult result) {
User existingUser = userService.findByEmail(user.getEmail());
if(existingUser != null) {
result.rejectValue("email", null, "this email is already used");
}
if (result.hasErrors()) {
return "register";
}
userService.saveNewUser(existingUser);
return "redirect:/";
}
}
UserService.java
public void saveNewUser(User user) {
User nuser = new User();
nuser.setUsername(user.getUsername());
nuser.setEmail(user.getEmail());
nuser.setPassword(user.getPassword());
nuser.setFirstName(user.getFirstName());
nuser.setLastName(user.getLastName());
// TODO: is this how you set the role of the user?
nuser.setRoles((Set<Role>) Arrays.asList(new Role("user")));
userRepository.save(nuser);
}
I solved this problem by changing two things; first, I created a custom saveNewUser() but JPA manages that better so I changed that to this.
UserService.java
public User save(User user) {
user.setRoles(new HashSet<Role> (Arrays.asList(new Role("user"))));
return userRepository.save(user);
}
I also had to change my endpoint to this
controller.java
#PostMapping("/register")
public String postRegister(User user) {
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String encodedPassword = passwordEncoder.encode(user.getPassword());
user.setPassword(encodedPassword);
userService.save(user);
return "redirect:/";
}

Update operation is not performing -Resolved [org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported]

I'm trying to develop an application in spring boot + thymeleaf, and I'm able to retrieve the logged in user details in the profile tab from the MySQL database, but when I try to change one or two field details (update) and hit the update button it is showing me an error message - Fri Sep 04 20:39:47 IST 2020
There was an unexpected error (type=Method Not Allowed, status=405).
Request method 'POST' not supported
see my controller code (I'm using #RestController annotated on top of the class)-
#RequestMapping(value = "/profile", method = RequestMethod.PUT)
public ModelAndView updateProfile(#ModelAttribute Customer customer, HttpSession session) {
ModelAndView model = new ModelAndView();
Customer exist = cRepo.findByCustEmail(customer.getCustEmail());
if(exist != null) {
if(exist.getCustEmail().equals(session.getAttribute("emailsession"))) {
cRepo.save(customer);
model.addObject("msg", "User Details has been successfully updated!!");
model.setViewName("profile");
}
}else {
model.addObject("exist", "Please enter correct email address!");
String email = (String) session.getAttribute("emailsession");
Customer cust = cRepo.findByCustEmail(email);
model.addObject("customer", cust);
model.setViewName("profile");
}
return model;
}
Thymleaf code (html) -
<div align="center" class="alert alert-success" th:if="${msg}" th:utext="${msg}"></div>
<div align="center" class="alert alert-danger" th:if="${exist}" th:utext="${exist}"></div>
<!-- Modal HTML -->
<div id="myModal">
<div class="modal-dialog modal-login">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Profile Details</h4>
</div>
<div class="modal-body">
<form name="myForm" th:action="#{/profile}" th:object="${customer}" method="post">
<div class="form-group">
<i class="fa fa-id-card"></i>
<input name="id" type="text" class="form-control" placeholder="Enter Id" th:field="${customer.custId}" disabled="true" required="required" />
</div>
<div class="form-group">
<i class="fa fa-user"></i>
<input name="name" type="text" class="form-control" placeholder="Enter Name" th:field="${customer.custName}" required="required" />
</div>
<div class="form-group">
<i class="fa fa-envelope"></i>
<input name="email" type="email" class="form-control" placeholder="Enter Email" th:field="${customer.custEmail}" required="required" />
</div>
<div class="form-group">
<i class="fa fa-lock"></i>
<input name="password" type="text" class="form-control" placeholder="Enter Password" th:field="${customer.custPassword}" required="required" />
</div>
<div class="form-group">
<input type="submit" class="btn btn-primary btn-block btn-lg" value="Update" />
</div>
</form>
</div>
</div>
</div>
</div>
I want when user login and visit he/she should be able to check his/her profile(which I'm able to do working code) and when the user wants to update few fields(1-2 based on choice) and hit update he/she should be able to update the details (not create new user or record) because when I use #Controller on top of class then this code work and create new user instead update.
Your controller is annotated with #RequestMapping(value = "/profile", method = RequestMethod.PUT) which makes it a PUT endpoint. However, your request is clearly a POST. If we look at your html form it contains method="post". HTML forms only support GET and POST as valid methods so you need to update your endpoint to be a POST endpoint.
tldr;
RequestMapping(value = "/profile", method = RequestMethod.PUT)
to
RequestMapping(value = "/profile", method = RequestMethod.POST)
You request mapping in is POST but Controller has set to accept request as PUT.
<form name="myForm" th:action="#{/profile}" th:object="${customer}" **method="post"**>
#RequestMapping(value = "/profile", method = **RequestMethod.PUT**)
Just keep these in similar way both should be same.
Please check what I find and resolve this.
#RequestMapping(value = "/profile" ,method = RequestMethod.POST)
public ModelAndView updateProfile(#ModelAttribute Customer customer, HttpSession session) {
ModelAndView model = new ModelAndView();
Customer exist = cRepo.findByCustEmail(customer.getCustEmail());
if(exist != null) {
if(exist.getCustEmail().equals(session.getAttribute("emailsession"))) {
**exist.setCustId(exist.getCustId());
exist.setCustName(customer.getCustName());
exist.setCustEmail(customer.getCustEmail());
exist.setCustPassword(customer.getCustPassword());**
cRepo.save(exist);
model.addObject("msg", "User Details has been successfully updated!!");
model.addObject("customer", exist);
model.setViewName("profile");
}
}else {
model.addObject("exist", "Please enter correct email address!");
String email = (String) session.getAttribute("emailsession");
Customer cust = cRepo.findByCustEmail(email);
model.addObject("customer", cust);
model.setViewName("profile");
}
return model;
}

Encryption method for cuba platform

Cuba has its own backend system to add users.
Now I need to write a user registration in the front-end.
The Cuba version I am using is 6.9
I know that this version of encryption is SHA1 : https://doc.cuba-platform.com/manual-6.9/login.html
Now my question is : I don't know how to set the encrypted password to the database.
I create an entity through the Metadata
User user = metadata.create(User.class);
user.setPassword(passWord);
I'm not sure that's the best option but I used the following code:
#Inject
protected PasswordEncryption passwordEncryption;
...
user.setPassword(passwordEncryption.getPasswordHash(user.getId(), password));
I think you just need to create html file with this code:
<div class="span6">
<h3>Login</h3>
<div th:if="${(param.error != null)}">
<p>Invalid username / password</p>
</div>
<form id="f" name="f" method="POST" action="login">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>
<div>
<div>
<label style="width: 80px" for="login">Login</label>
<input type="text" id="login" name="login" value=""/>
</div>
<div>
<label style="width: 80px" for="password">Password</label>
<input type="password" id="password" name="password" value=""/>
</div>
</div>
<button type="submit">Login</button>
</form>
</div>
and then Request it from Controller:
#RequestMapping(value = "/", method = RequestMethod.GET)
public String index(Model model) {
if (PortalSessionProvider.getUserSession().isAuthenticated()) {
LoadContext l = new LoadContext(User.class);
l.setQueryString("select u from sec$User u");
model.addAttribute("users", dataService.loadList(l));
}
return "index";
}

Displaying "Passwords don't match" custom annotation message

How to display this message from custom class type annotation in Spring as a warning message in HTML form? I am using Spring Boot MVC and Thymeleaf. Thymeleaf can handle field errors and global errors that are not associated with any specific fields in the form, but still exist. If this custom annotation finds that passwords don't match is this global error or field error? How can I show this message?
Custom annotation:
#Target({ TYPE, ANNOTATION_TYPE })
#Retention(RUNTIME)
#Constraint(validatedBy = PasswordMatchesValidator.class)
#Documented
public #interface PasswordMatches {
String message() default "Passwords don't match";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Validator class:
public class PasswordMatchesValidator implements ConstraintValidator<PasswordMatches, Object> {
#Override
public void initialize(PasswordMatches passwordMatches) {
}
#Override
public boolean isValid(Object obj, ConstraintValidatorContext context) {
UserDto userDto = (UserDto) obj;
return userDto.getPassword().equals(userDto.getMatchingPassword());
}
}
Post Controller
#PostMapping(value = "/register")
public ModelAndView processRegistration(ModelAndView modelAndView, #Valid #ModelAttribute ("userDto") UserDto userDto,
BindingResult bindingResult, HttpServletRequest request, Errors errors) {
// Lookup user in database by e-mail
User userExists = userService.findByEmail(userDto.getEmail());
System.out.println(userExists);
if (userExists != null) {
modelAndView.addObject("alreadyRegisteredMessage", "Oops! There is already a user registered with the email provided.");
modelAndView.setViewName("register");
bindingResult.reject("email");
}
if (bindingResult.hasErrors()) {
modelAndView.setViewName("register");
} else { // new user so we create user and send confirmation e-mail
User user = userService.createNewAccount(userDto);
user.setEnabled(false);
userService.saveUser(user);
modelAndView.addObject("confirmationMessage", "A confirmation e-mail has been sent to " + userDto.getEmail());
modelAndView.setViewName("registered");
}
}
HTML form:
<div class="container">
<div class="row">
<div class="card col-6 mx-auto" style="width: 20rem; margin: 20px;">
<div class="card-body">
<h4 class="card-title">Registration form:</h4>
<form th:action="#{register}" th:object="${userDto}" th:method="post" method="post" action="register">
<div class="form-group">
<label th:for="name" for="Name">Name</label>
<input type="text" class="form-control" th:field="*{name}" id="name" placeholder="Enter name">
<p class="text-danger" th:if="${#fields.hasErrors('name')}" th:errors="*{name}"></p>
</div>
<div class="form-group">
<label th:for="surname" for="Surname">Surname</label>
<input type="text" class="form-control" th:field="*{surname}" id="surname" placeholder="Enter surname">
<p class="text-danger" th:if="${#fields.hasErrors('surname')}" th:errors="*{surname}"></p>
</div>
<div class="form-group">
<label th:for="username" for="Username">Username</label>
<input type="text" class="form-control" th:field="*{username}" id="username" placeholder="Enter username">
<p class="text-danger" th:if="${#fields.hasErrors('username')}" th:errors="*{username}"></p>
</div>
<div class="form-group">
<label th:for="email" for="Email">Email address</label>
<input type="email" class="form-control" th:field="*{email}" id="email" aria-describedby="emailHelp" placeholder="Enter email">
<small id="emailHelp" class="form-text text-muted">We'll never share your email with anyone else.</small>
<p class="text-danger"th:if="${#fields.hasErrors('email')}" th:errors="*{email}"></p>
<p class="text-danger" th:text="${alreadyRegisteredMessage}"></p>
</div>
<div class="form-group">
<label th:for="password" for="Password">Password</label>
<input type="password" class="form-control" th:field="*{password}" id="password" placeholder="Password">
<p class="text-danger"th:if="${#fields.hasErrors('password')}" th:errors="*{password}"></p>
</div>
<div class="form-group">
<label th:for="matchingPassword" for="matchingPassword">Confirm Password</label>
<input type="password" class="form-control" th:field="*{matchingPassword}" id="matchingPassword" placeholder="Password">
<p class="text-danger"th:if="${#fields.hasErrors('matchingPassword')}" th:errors="*{matchingPassword}"></p>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
</div>
</div>
</div>
You can customize the PasswordMatchesValidator class to display the warning message.
public class PasswordMatchesValidator implements ConstraintValidator<PasswordMatches, Object> {
private String message;
#Override
public void initialize(PasswordMatches passwordMatches) {
this.message = passwordMatches.message();
}
#Override
public boolean isValid(Object obj, ConstraintValidatorContext context) {
final UserDto userDto = (UserDto) obj;
boolean isValid = userDto.getPassword().equals(userDto.getMatchingPassword());
if (!isValid) {
context.disableDefaultConstraintViolation();
context
.buildConstraintViolationWithTemplate( message )
.addPropertyNode( "matchingPassword" ).addConstraintViolation();
}
return isValid;
}
}
The solution is to include global errors Thymeleaf syntax in HTML form. But for some reason single quote in this message: "Passwords don't match", doesn't get rendered. The answer to this problem is here.

Categories

Resources