Problem
I have the following constraints on userUuid and itemUuid:
Both strings must not be null.
Both strings must be UUIDs (eg. f1aecbba-d454-40fd-83d6-a547ff6ff09e).
The composition (userUuid, itemUuid) must be unique.
I tried to implement the validation in my controller like:
#RestController
#Validated // (1)
public class CartItemController {
#PostMapping("/me/carts/{itemUuid}")
#ResponseStatus(HttpStatus.CREATED)
public void addItem(#PathVariable("itemUuid") String itemUuid,
Authentication auth) {
CartItemId id = getCartItemId(getUserUuidFrom(auth), itemUuid);
...
}
#Unique // (4)
public CartItemId getCartItemId(#NotNull #Uuid String userUuid, // (2)
#NotNull #Uuid String itemUuid) { // (3)
return new CartItemId(userUuid, itemUuid);
}
...
}
#Uuid and #Unique are custom constraints. Method validation is enabled in (1). (2) are the contraints for the user UUID. (3) are the constraints for the item UUID. The unique constraint is applied to the returned CartItemId in (4). However, the parameters and the return value are never validated. Neither for the standard #NotNull constraint nor for my custom constraints. I receive HTTP status 201 Created instead of 400 Bad Request.
What am I doing wrong?
Stuff that works
The following code works for the item UUID:
#RestController
#Validated
public class CartItemController {
#PostMapping("/me/{itemUuid}")
#ResponseStatus(HttpStatus.CREATED)
public void addItem(#PathVariable("itemUuid") #Uuid String itemUuid, // (1)
Authentication auth) {
...
}
}
Adding the #Uuid to the path variable parameter works. Values like anInvalidUuid are rejected. I also tested the #Unique constraint in other use cases and it worked perfectly.
What is the difference between addItem() and toId()?
Versions
I am using Java 1.8 and Spring Boot 2.0.0.RELEASE. org.hibernate.validator:hibernate-validator:6.0.7.Final is on my classpath.
Validation of method arguments is based on AOP: a validating proxy intercepts the method call and validates the argument before delegating (if everything is valid), to the actual method.
You're calling the getCartItemId() method from another method of the same class. So the method call doesn't go through the proxy. Only inter-bean calls can be intercepted.
So, in short, getCartItemId should be in a separate bean, injected into your controller.
Related
I am trying to solve some vulnerabilities issues, and I have one that I couldn't solve it, I tried to add #Valid annotation in sync method but same error, this is the description from fortify:
The framework binder used for binding the HTTP request parameters to
the model class has not been explicitly configured to allow, or
disallow certain attributes.
To ease development and increase productivity, most modern frameworks
allow an object to be automatically instantiated and populated with
the HTTP request parameters whose names match an attribute of the
class to be bound. Automatic instantiation and population of objects
speeds up development, but can lead to serious problems if implemented
without caution. Any attribute in the bound classes, or nested
classes, will be automatically bound to the HTTP request parameters.
Therefore, malicious users will be able to assign a value to any
attribute in bound or nested classes, even if they are not exposed to
the client through web forms or API contracts.
The error I am getting in this line:
public ResponseClass sync(#BeanParam MyClassRequest request) throws
Exception {
MyClassResource.java
#Api(tags = "Relay")
#Stateless
public class MyClassResource extends AbstractService<MyClassRequest, ResponseClass> {
#EJB
private MyClassService myClassService;
#POST
#Path("/api/v1/service")
#Produces({"application/json"})
#ApiOperation(value = "Processes Conn",
response = ResponseClass.class, responseContainer = "ResponseClass", hidden = true)
#Override
public ResponseClass sync(#BeanParam MyClassRequest request) throws Exception {
myClassService.processFeed(request);
return new RelayResponse(HttpStatuses.ACCEPTED.getStatus());
}
MyClassRequest.java
In this file I have tried #FormParam("ccc") but same
public class MyClassRequest extends RelayRequest {
public MyClassRequest() {
super.setMessageType("not required");
}
private String myData;
private String conneRid;
private String connectionCreatedDate;
If someone could give some hint that how I can solve it, I will really appreciate it.
Do you expect all fields to be present in request? You are using #Valid annotation but there are no validation annotations in MyClassRequest model. Try to add some validation annotations like #JsonIgnore for non mandatory fields. Or #JsonInclude on class. If this does not help, may be also try explicitly adding #JsonProperty on each field.
We have a need to have certain objects be wrapped in an optional because we need to know three states:
Missing (not present)
Null (present but null)
Data (present with data)
Currently we are wrapping these fields with Optional because spring rest endpoint will give us these states as null, Optional.empty() and Optional.of(value).
However, it does not appear that the validations are working. Do I need to do something different than just add #Valid, #Pattern, #NotNull, etc??
Note: there is a more detail breakdown of the need in this question (Java Spring (Jackson) Annotation for Not Present, Present but Null, and Present with Value)
Add javax.validation dependency
Below are the 3 steps to add validation.
Add #NonNull on the data member in the POJO class
class Employee
{
#NonNull
String name;
}
Create following method
private void validate(Employee emp){
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<Employee>> violations = validator.validate(emp)
for(ConstraintViolation<Employee> violation: violations) {
//Your action. May be throw exception
}
}
Add #Valid where the object is used and invoke the validate method
void method(#Valid Employee emp){
validate(emp);
}
So the best solution that I found was to use Jakarta validations instead of Javax because the Jakarta will look into the optional field and apply the validation annotations.
I have a DTO class with fields that either have javax or custom constraints put on them so everytime I hit this endpoint I am guaranteed to take in data that meets my requirements.
#PostMapping
public ResponseEntity<SomeResource> create(#Valid #RequestBody SomeDTO someDTO)
For reasons I do not want to go into, I am forced to validate this incoming data elsewhere (preferably in a separate service method) so I have tried doing:
#PostMapping
public ResponseEntity<SomeResource> create(#RequestBody SomeDTO someDTO) {
someService.validate(someDTO);
}
where the called method's signature is defined as
validate(#Valid SomeDTO someDTO)
Though I quickly figured out this does not actually do any other argument validation other than user input. With the Spring annotations not being particularly helpful, are there any other annotations out there that can validate an object passed in as a parameter to ensure the constraints are not violated?
I'm using Constraint Validators (JSR-303) on my beans in a Spring MVC Controller. Given the following java bean:
public class EntityDTO {
protected Long id;
protected Integer version;
protected String someOtherField;
// getters and setters
}
I have 2 different types of constraint validation I would like to perform. Either both id and version are null, or both are non-null.
I can create 2 different constraint validators and assign to 2 different annotations: DTOEntityFieldsEmpty & DTOEntityFieldsNotEmpty. But then I have to specify the validator annotation at the bean level.
#DTOEntityFieldsEmpty
public class EntityDTO {
....
}
However, I'm looking to specify the validator that I want to use in the actual controller method level. Under normal circumstances, my method would be:
public void updateData( #RequestBody #Valid EntityDTO dto){
...
}
where the #Valid annotation will apply the Validator that is defined in the EntityDTO object. But I'm looking to see if there is a way I can either pass a parameter at the #Valid request, or specify the validator to use.
// #Valid annotation not supported this way
public void updateData( #RequestBody #Valid(validator=DTOEntityFieldsEmpty.class) EntityDTO dto){
...
}
Is there anything I can do to get around this? I realize that I can use Spring's #InitBinder, but that will bind a validator to the entire Controller, and not just one specific method.
I've checked both JSR-303 1.0 and 1.1, and don't see anything that jumps out at me to handle this circumstance. Similarly, I can't find anything in the Spring 3 or 4 docs either. I wonder if there might be a way using Group validation, but not entirely sure. I would need to be able to know which validator was successful or failed in the controller, and that seems a little hacky to me.
You can use Validation Groupes. Therefore you need to assign the constraints at the fields to groups and then you need to tell spring which group to validate (this can be one or more groupes), therefore Spring 3.1 introduced the #Validated annotation.
public interface GroupA { } //empty marker interface
public interface GroupB { }
#DTOEntityFieldsEmpty(groups=GroupA.class)
#DTOEntityFieldsNotEmpty(groups=GroupB.class)
public class EntityDTO {
....
}
public void updateData(
#RequestBody #Validated({ GroupA.class }) EntityDTO dto){
...
}
I was wondering if it is possible to chain #ModelAttribute methods by having an #ModelAttribute annotated, but not request mapped, method use another ModelAttribute in the method signature.
This would be in a controller.
ie
#ModelAttribute("attrOne")
public AttrOne getAttrOne() {
return service.getAttOne();
}
#ModelAttribute("attrTwo")
public AttrTwo getAttrTwo(#ModelAttribute("attrOne") AttrOne attrOne){
return anotherservice.getAttrTwo(attrOne);
}
Then if there was a request mapped method that did this:
#RequestMapping(method=RequestMethod.GET)
public String doSomething(#ModelAttribute("attrTwo") AttrTwo attrTwo )
would this work?
I seem to get a null object for AttrOne in the second annotated method... as the first annotated method is not called by the second one...
Cheers
I ran into the same situation by learning from the spring documention:
#ModelAttribute is also used at the method level [..]. For this usage the method signature can contain the same types as documented above for the #RequestMapping annotation.
I found SPR-6299 which faces this problem. In the comments you can find a workaround by providing only one #ModelAttribute annotated method which sets the attributes into the model:
#ModelAttribute
public void populateModel(Model model) {
model.addAttribute("attrOne", getAttrOne());
model.addAttribute("attrTwo", getAttrTwo());
}
According to SPR-6299, this will be possible in Spring 4.1 RC1 or later.