Validate Additional parameters in Requestbody for Springboot request and throw 400 - spring

#Data
public class Employee{
#NotNull
#NotBlank
private string id;
#NotNull
#NotBlank
private string name;
}
#RestController
#Validated
class EmployeeController{
#postMapping(consumes="json", produces="json")
public ResponseEntity getEmployee(#Valid #RequestBody Employee){
return response;
}
}
Added Exceptional Handler with MethodArgumentNotvalidException.
positive Validation works fine, Whenever we check with id & name.
Expectation is, if we add any additional parameters along with id and name, it should throw the 400
sample input :
{
"abc":"xyz",
"id":"09e240",
"name":"Billa"
}
Expected Output : 400 bad request

The validation doesn't occur until after the deserialization into the Employee object has occurred; therefore, I don't believe you can do this with the validation API.
What you want to do is force the deserialization to fail on unknown properties. Create a bean of type ObjectMapper and configure it to fail deserialization when it encounters unknown properties.
#Bean
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true);
return objectMapper;
}
This will throw an UnrecognizedPropertyException, then you can use ControllerAdvice to map this to a 400. By the way, I suspect that you have already created this bean somewhere as the default behavior for ObjectMapper is set to FAIL_ON_UNKNOWN_PROPERTIES -> true.
The controller advice to map the UnrecognizedPropertyException will look like this:
import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
#ControllerAdvice
public class Advice {
#ExceptionHandler(UnrecognizedPropertyException.class)
public ResponseEntity<?> handleDeserializationErrors(UnrecognizedPropertyException unrecognizedPropertyException) {
return ResponseEntity.badRequest().body(unrecognizedPropertyException.getMessage());
}
}

Related

How do I set the HttpStatus code when using #ResponseBody?

In a SpringBoot Controller class, my APIs usually return a ResponseEntity with a body and a status code. But I can apparently dispense with the ResponseEntity by annotating my controller method with #ResponseBody, like this:
#Controller
public class DemoController
{
#Autowired
StudentService studentService;
#GetMapping("/student")
#ResponseBody
Student getStudent(#RequestParam id) {
return studentService.getStudent(id);
}
}
If my service throws an exception, I can return a custom HTTP status by throwing a ResponseStatusException, but it's not clear how to specify the HTTP status for a valid response. How would I specify this? Or how does it decide what to use?

How to make sure that the content of the request's parameter is safe?

This is my first time writing something in Spring. I add to the code already in the repo a small service that accepts a request parameter from the client and passes it to a third-party API.
The service contains two classes, not counting the interface:
#RestController
#RequestMapping("/store/search")
#CrossOrigin("*")
public class APToid {
private AptoidSearch aptoidSearch;
#GetMapping
public ResponseEntity<String> searchInAptoidStore(#RequestParam("query") String query) {
return aptoidSearch.aptoidSearch(query);
}
}
and
#Service
public class AptoidSearchImpl implements AptoidSearch {
#Autowired
private APToidUriConfiguration configuration;
#Override
public ResponseEntity<String> aptoidSearch(String query) {
RestTemplate restTemplate = new RestTemplate();
return restTemplate.getForEntity(configuration.getUri() + query, String.class);
}
}
The query string, as you can see, is taken directly from the client. What are some ways to make sure the client isn't giving me some dangerous shit, like exploit, etc?

Spring Cloud Gateway : actuator refresh does not reload properties

I am actually working on a project with Spring Cloud Gateway.
I have a Configuration class which gets its Properties from a custom PropretySourceFactory. I want to make a hot reload of the properties so I call actuator/refresh (curl localhost:8080/actuator/refresh -d {}H "Content-Type: application/json") but it does not reload my configuration properties. No error or exceptions.
Here is the Configuration class:
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
#Configuration
#ConfigurationProperties
#PropertySource(value="", factory=NatsPropertySourceFactory.class)
public class NatsConfiguration {
private String apiKey;
private String apiKeyConsumer;
private String apiKeyValidity;
private String apiKeyCreation;
private int ratelimitMaxTokens;
private int ratelimitReplenishFrequency;
private int ratelimitReplenishRate;
// Getters and setter
//...
}
value is empty on PropertySource because I will not get my configuration from a file but from a message queue.
and the NatsPropertySourceFactory:
public class NatsPropertySourceFactory implements PropertySourceFactory{
final private NatsConfigurationService natsConfigurationService = new NatsConfigurationServiceImpl();
#Override
public PropertySource<?> createPropertySource(String arg0, EncodedResource arg1) throws IOException {
MapPropertySource result = null;
Logger log = LoggerFactory.getLogger(this.getClass());
try {
result = new MapPropertySource("nats", natsConfigurationService.getConfiguration());
} catch (ConfigurationException e) {
log.error("RECUPERATION DE CONFIGURATION DEPUIS NATS EN ERREUR", e);
System.exit(1);
}
return result;
}
}
My properties are not used with #Value, so I should not need #RefreshScope.
When I call /actuator/refresh the NatsConfiguration class is not recreated.
For information I use webflux with SpringSecurity (actuator urls are permitAll: pathMatchers("/actuator/**").permitAll())
#EnableWebFluxSecurity
#EnableWebFlux
public class SecurityConfiguration implements WebFluxConfigurer {
Where am I wrong?
By the way, I found the exact behaviour of /actuator/refresh: it reinstanciates the #Configuration class but does nothing for the PropertySourceFactory.
I have found a workaround: I created a REST Controler which calls the createPropertySource method of the PropertySourceFactory and then calls the /actuator/refresh url. It does exactly what I wanted: the #Configuration class is up to date with the new properties given by the PropertySourceFactory.

Spring Data Rest: #Autowire in Custom JsonDeserializer

I am trying to autowire a component into a custom JsonDeserializer but cannot get it right even with the following suggestions I found:
Autowiring in JsonDeserializer: SpringBeanAutowiringSupport vs HandlerInstantiator
Right way to write JSON deserializer in Spring or extend it
How to customise the Jackson JSON mapper implicitly used by Spring Boot?
Spring Boot Autowiring of JsonDeserializer in Integration test
My final goal is to accept URLs to resources in different microservices and store only the ID of the resource locally. But I don't want to just extract the ID from the URL but also verify that the rest of the URL is correct.
I have tried many things and lost track a bit of what I tried but I believe I tried everything mentioned in the links above. I created tons of beans for SpringHandlerInstantiator, Jackson2ObjectMapperBuilder, MappingJackson2HttpMessageConverter, RestTemplate and others and also tried with setting the SpringHandlerInstantiator in RepositoryRestConfigurer#configureJacksonObjectMapper.
I am using Spring Boot 2.1.6.RELEASE which makes me think something might have changed since some of the linked threads are quite old.
Here's my last attempt:
#Configuration
public class JacksonConfig {
#Bean
public HandlerInstantiator handlerInstantiator(ApplicationContext applicationContext) {
return new SpringHandlerInstantiator(applicationContext.getAutowireCapableBeanFactory());
}
}
#Configuration
public class RestConfiguration implements RepositoryRestConfigurer {
#Autowired
private Validator validator;
#Autowired
private HandlerInstantiator handlerInstantiator;
#Override
public void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener validatingListener) {
validatingListener.addValidator("beforeCreate", validator);
validatingListener.addValidator("beforeSave", validator);
}
#Override
public void configureJacksonObjectMapper(ObjectMapper objectMapper) {
objectMapper.setHandlerInstantiator(handlerInstantiator);
}
}
#Component
public class RestResourceURLSerializer extends JsonDeserializer<Long> {
#Autowired
private MyConfig config;
#Override
public Long deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
ServiceConfig serviceConfig = config.getServices().get("identity");
URI serviceUri = serviceConfig.getExternalUrl();
String servicePath = serviceUri.getPath();
URL givenUrl = p.readValueAs(URL.class);
String givenPath = givenUrl.getPath();
if (servicePath.equals(givenPath)) {
return Long.parseLong(givenPath.substring(givenPath.lastIndexOf('/') + 1));
}
return null;
}
}
I keep getting a NullPointerException POSTing something to the API endpoint that is deserialized with the JsonDeserializer above.
I was able to solve a similar problem by marking my deserializer constructor accept a parameter (and therefore removing the empty constructor) and marking constructor as #Autowired.
public class MyDeserializer extends JsonDeserializer<MyEntity> {
private final MyBean bean;
// no default constructor
#Autowired
public MyDeserializer(MyBean bean){
this.bean = bean
}
...
}
#JsonDeserialize(using = MyDeserializer.class)
public class MyEntity{...}
My entity is marked with annotation #JsonDeserialize so I don't have to explicitly register it with ObjectMapper.

Unresolved Placeholder Validation for Spring Boot Configuration Properties

Given some application configuration with an unresolvable placeholder, like the following application.yml
my:
thing: ${missing-placeholder}/whatever
When I use #Value annotations, the placeholders in the configuration file are validated, so in this case:
package com.test;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
#Component
public class PropValues {
#Value("${my.thing}") String thing;
public String getThing() { return thing; }
}
I get an IllegalArgumentException: Could not resolve placeholder 'missing-placeholder' in value "${missing-placeholder}/whatever". This is because the value is being set directly by AbstractBeanFactory.resolveEmbeddedValue and there is nothing to catch the exception thrown by PropertyPlaceholderHelper.parseStringValue
However, looking to move to #ConfigurationProperties style I noticed that this validation is missing, for example in this case:
package com.test;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
#ConfigurationProperties(prefix = "my")
public class Props {
private String thing;
public String getThing() { return thing; }
public void setThing(String thing) { this.thing = thing; }
}
there is no exception. I can see PropertySourcesPropertyValues.getEnumerableProperty catches the exception with the comment // Probably could not resolve placeholders, ignore it here and gathers the invalid value into its internal map. Subsequent data binding does not check for unresolved placeholders.
I checked that simply applying the #Validated and #Valid annotations to the class and field do not help.
Is there any way to preserve the behaviour of throwing an exception on unresolved placeholders with ConfigurationProperties binding?
Apparently there are no better solutions. At least this is kind of nicer than afterPropertiesSet().
#Data
#Validated // enables javax.validation JSR-303
#ConfigurationProperties("my.config")
public static class ConfigProperties {
// with #ConfigurationProperties (differently than #Value) there is no exception if a placeholder is NOT RESOLVED. So manual validation is required!
#Pattern(regexp = ".*\$\{.*", message = "unresolved placeholder")
private String uri;
// ...
}
UPDATE: I got the regex wrong the first time. It as to match the entire input (not just java.util.regex.Matcher#find()).
The correct regex to pass in #Pattern annotation is ^(?!\\$\\{).+
#Validated
#ConfigurationProperties("my.config")
public class ConfigProperties {
#Pattern(regexp = "^(?!\\$\\{).+", message = "unresolved placeholder")
private String uri;
// ...
}
I had the same issue exactly 10 minutes ago!
Try to add this bean in your configuration:
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
propertySourcesPlaceholderConfigurer.setIgnoreUnresolvablePlaceholders(true);
return propertySourcesPlaceholderConfigurer;
}

Resources