Stop some validations at the persist time in Spring - validation

Is there a way to stop some validations to be executed at the time of data persist.
I know about
spring.jpa.properties.javax.persistence.validation.mode=none
but I believe that disables all validations, I just want to disable it for some of the fields (Password specifically, as it is encoded by that time and then pattern doesn't match). Thanks!
Update(More Details):
#Pattern(regexp = "^[A-Za-z0-9_#!]+$")
private String password;
I have a password field validated using the above pattern (A-Z, a-z, 0-9 and _,#,!). In the controller it validates success by below code.
#RequestMapping(value = "/adduser", method = RequestMethod.POST)
public ModelAndView signUp(ModelAndView modelAndView, #ModelAttribute #Valid LoginUser loginUser, BindingResult bindingResult) {
LoginUser loginUserAdded = null;
if (!bindingResult.hasErrors()) {
loginUser.setPassword(passwordEncoder.encode(loginUser.getPassword()));
loginUserAdded = createUser(loginUser);
....
But then before persist I encode the password and then it throws error while calling save method in JpaRepository because the password value has been changed by the encoder and it doesnt satisfy the pattern validation applied to it.
Now I am looking for a way by which I can disable validation on this field at the time of persist.

Related

Ignore field from bindingResult validation

I'm using SpringBoot 2.1.3 (Embedded Tomcat) + Thymeleaf 3 + java 8. I have a problem regarding validation of a UserDTO that is similar to follow:
#Data
public class UserDTO {
#NotNull
private String name;
#NotNull
private String surname;
.....
#NotBlank
#Email
#UniqueEmailConstraint // this is a custom validator
private String email;
#NotNull
private String pass;
.......
}
#UniqueEmailConstraint check inside the DB if the email is present or not (just one email for account are admitted). Than I have 2 controller, one for inserting user and another one for updating user
#PostMapping("/save-user")
String saveUser(#ModelAttribute("userDTO") #Valid UserDto userDto, BindingResult bindingResult, Model model) {
if (bindingResult.hasErrors()) {
return "fragment/form-user";
}
and similar one with some other function:
#PostMapping("/update-user")
String updateUser(#ModelAttribute("userDTO") #Valid UserDto userDto, BindingResult bindingResult, Model model) {
if (bindingResult.hasErrors()) {
return "fragment/form-user";
}
The problem is that when I selecting a user to modify it, a thymeleaf view is open and show me all data inserted as expected (mail included). If I try to modify another field, for example Address and click submit my controller show an errors because it find the email on DB.
Question is, is there a way to ignore certain field on bindingResult? Because of I would like to ignore the #UniqueMailConstraint error on second controller Validation.
Thanks all
I had the a similar problem hope this helps:
Lets say there is an employee HTML form with two buttons.
One of the buttons should validate the whole form, another button should validate just one single field and ignore the validation for the rest.
#RequestMapping(method = RequestMethod.POST, value = "saveEmployee", params = "action=save")
public ModelAndView saveEmployee(#Valid #ModelAttribute("employee") EmployeeDTO employee, BindingResult bindingResult, final HttpServletRequest request, final Model model) {
ModelAndView mav = new ModelAndView();
//Create a new BindingResult with zero errors
BindingResult newBindingResult = new BeanPropertyBindingResult(employee, "employee");
//Add to the new BindingResult the error which is caused by the field 'employeeNumber'
newBindingResult.addError(bindingResult.getFieldError("employeeNumber"));
//If required, more fields can be added to the new BindingResult
//Check if the new BindingResult contains errors -> only the fields added above.
if (newBindingResult.hasErrors()) {
//Do this, if the new BindingResult contains validation errors
} else {
//Do that, if the new BindingResult does not contain errors validation errors
//If your form contains other validation errors for fields other than 'employeeNumber', the validation for them will be ignored.
}
//Set the view and return it.
mav.setViewName(xxx);
return mav;
}

Invoking service with #RequestParam and #RequestBody using postman client

Was trying to invoke the service
http://IP:8080/PQRS/LMN/XYZ/runTest/scheduledautomation/1/XYZ
with below JSON String
[ {"paramName":"TEST_TARGET_IDENTIFIER","paramValue":"ETest"},{"paramName":"TEST_SOURCE_ENTRY_IDENTIFIER","paramValue":"com.pack.etest"}]
#ResponseStatus(value = HttpStatus.NO_CONTENT)
#RequestMapping(value = "/runTest/scheduledautomation/{runId}/{testEngine}", method = RequestMethod.POST)
public void runScheduledAutomatedTest(#RequestParam String cronExpresssion,
#RequestParam(required = false) #DateTimeFormat(iso = ISO.DATE_TIME) LocalDateTime endTime,
#PathVariable Integer runId,
#PathVariable TestEngine testEngine,
#RequestBody List<TestEngineParam> testEngineParams) throws Exception { //Some Code }
Response :
Required String parameter 'cronExpresssion' is not present
how to invoke mixed #RequestParam and #RequestBody services on postman client ?
I fear you want a little bit too much: RequestParam, RequestBody AND the whole thing as a REST query. At least two of the three things are mutually exclusive.
I think you could even get Postmaster to do this by modifying the called URL to:
http://IP:8080/PQRS/LMN/XYZ/runTest/scheduledautomation/1/XYZ?cronExpression=your-expression
Of course this would ruin your REST interface, but as I said: your handler method is a little bit "over-ambitious".

obscuring url strings in spring mvc

How do I obscure the values of fields used in url strings in a spring mvc web app?
For example, if I want to send the record with recordID=1 into the view, I give the user a hyperlink with the following url:
https://myapp.com/urlpattern?recordID=1
As you can see, this not only exposes the recordID=1, it also tempts a malicious user to start typing other numbers to mine other records such as recordID=5 or recordID=9.
Does the spring framework or spring security have a built-in way of encrypting url strings? Or do I need to change the id values in the underlying database using hibernate?
The controller code for the above url pattern is:
#RequestMapping(value = "/urlpattern", method = RequestMethod.GET)
public String processUrlPattern(#RequestParam("recordID") String recordId,
HttpServletRequest request, BindingResult result, Map<String, Object> model) {
Long recId = Long.valueOf(recordId).longValue();
RecordObject sel_record = this.appService.findRecordById(recId);
model.put("sel_record", sel_record);
return "foldername/jspname";
}
Note that all entities in the app inherit from the same BaseEntity whose id-generating code is as follows:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
#DiscriminatorFormula("(CASE WHEN dtype IS NULL THEN 'BaseEntity' ELSE dtype END)")
#org.hibernate.annotations.DiscriminatorOptions(force=true)
public abstract class BaseEntity {
#Transient
private String dtype = this.getClass().getSimpleName();
#Id
#GeneratedValue(strategy=GenerationType.TABLE, generator="TBL_GEN")
#TableGenerator(
name="TBL_GEN",
table="GENERATOR_TABLE",
pkColumnName = "mykey",
valueColumnName = "hi",
pkColumnValue="id",
allocationSize=20
)
protected Integer id;
//other stuff
}
NOTE: All the users are authenticated/authorized using Spring security. However, the data is very sensitive, and it is important that no one be able to manipulate url strings.
Use HDIV, it does this out of the box:
http://hdiv.org/hdiv-documentation-single/doc.html
"A6 (Sensitive data exposure) : HDIV offers a confidentially property to all data generated at sever side. That is to say, HDIV replace original parameter values generated at server side by relative values (0,1,2,4, etc.) that avoid exposing critical data to the client side."

400 Bad request with Hibernate #Valid

I have a strange behaviour when I validate my form.
As soon as I add the Hibernate #Valid annotation, Tomcat consided my request as "bad" if the posted data are not valid. If the data are valid, no worries.
I use:
Tomcat 7.0.52
Javax Validation api 1.1.0.Final
Hibernate Validator 5.1.0.Final
Spring 4.0.3.RELEASE
At the moment, I do a really simple validation:
public class RemoveCacheElementForm {
#NotBlank(message = "Please select a cache name.")
private String name;
#NotBlank(message = "Please select a cache entry key.")
private String key;
The Spring controller:
/**
* Handler to remove one cached elements from the specified cache.
*
* #return the view.
*/
#RequestMapping(value = CACHE_REMOVE, method = RequestMethod.POST)
public String removeCachedElement(ModelMap model, #Valid #ModelAttribute(FORM_NAME) RemoveCacheElementForm form) {
model.addAttribute("removeElementResult", CacheUtils.removeCachedElement(form.getName(), form.getKey()));
initializeModel(model);
return CACHE_ADMIN_PAGE;
}
When I remove #Valid annotation, no worries too.
Anyone has an idea?
Thanks a lot for your help! :-)
Try changing your code to
#RequestMapping(value = CACHE_REMOVE, method = RequestMethod.POST)
public String removeCachedElement(ModelMap model, #Valid #ModelAttribute(FORM_NAME) RemoveCacheElementForm form, BindingResult bindingResult) {
model.addAttribute("removeElementResult", CacheUtils.removeCachedElement(form.getName(), form.getKey()));
initializeModel(model);
return CACHE_ADMIN_PAGE;
}

Spring: Same object, different validation

I have an object called User where I save all the data of the User. I have some annotations to perform validation and it works fine.
public class User{
#NotEmpty
#Email
#Size(max=100)
#Column(name="username", length=100, nullable=false, unique=true)
private String username;
#NotEmpty
#Size(min=5, max=40)
#Column(name="password", length=40, nullable=false)
private String password;
#Size(min=5, max=40)
#Transient
private String newPassword;
// other attributes ,getters and setters
}
I have two different forms (each one in a different page). In the first one I ask for username and password to create the user, so both of them are compulsory.
In the second form I show the information about the user: username, other data (which will be validated as well) andthe password and newPassword. If the newPassword is set and the password is the same as the user has I'll change the user's password, but if they are left empty it means I shouldn't change the password.
The problem is that I have two forms relating to the same object where there is a different validation for the field password. In the first one it must be not empty, but in the second one it can be empty.
In the controller I validate the object in this way:
public String getUserDetails(#Valid #ModelAttribute("User") User user, BindingResult result, Model model){
if(result.hasErrors()){
//There have been errors
}
...
}
but is password is empty there will be an error.
Is there any way to perform a validation only in some fields of the object?
Can I, at least, remove any validation error after the validation?
What is the best practice in this case?
Thanks
You can use JSR-303 constraint groups to achieve this.
public class User {
public interface GroupNewUser {};
#NotEmpty
private String username;
#NotEmpty(groups = {GroupNewUser.class});
private String password;
// ...
}
Then in the controller, instead of using #Valid, use Spring's #Validated annotation which allows specifying a constraint group to apply.
See this blog post and this one also for more information.
There is also this superb post for Spring usage.
This is the problem with declarative validation, it's not very easy to do this sort of thing.
The easiest solution is to remove the validation annotations from the password field, and validate that field manually in the controller. The BindingResult class has methods on it for you to explicitly mark fields as invalid.
Another alternative would be to create two subclasses of the form class, each with its own password field, one with validation annotations, and one without, and use the appropriate one in the appropriate place.
A few pointers. I'm not sure they will be useful, though:
Spring docs say that:
you may call binder.setValidator(Validator) within a #Controller's #InitBinder callback. This allows you to configure a Validator instance per #Controller class
javax.validation has two *Context interfaces. Get into more details with them to see whether different validation can be achieved in different contexts.
instead of using hasErrors() function, use hasFieldErrors(fieldname) and only validate particular fields required by the form.

Resources