Escaping specific strings in spring-mvc - java

I'm using spring MVC (spring 3.1.0).
I would like to html encode every string output from my system due to XSS.
But there are outputs (like links) that I don't want to encode in that manner - in order to do that I've decided to use ContextualSerializer and create a class that implements it.
The method createContextual checks for existence of a custom annotation (#NoHTMLEscap) and determine the JsonSerializer to return.
Here is my annotation:
#Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#JacksonAnnotation
public #interface NoHTMLEscap {
}
I tried it and it works just great - on POJOs, but here is a case that fails:
#Controller
#RequestMapping(value = "/something")
public class MyController {
...
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
#ResponseBody
#ResponseStatus(value = HttpStatus.OK)
public String getName(#PathVariable String id) {
return "Here is your answer";
}
}
Spring sends to jackson the returned value and since this method returns String and not POJO - there is no way to check the existence of my annotation - so I cannot actually use my annotation there...
Is there a way to get this information to Jackson's serializer?
I'm not sure but I thought that maybe I need to use a spring specific converter which will never escape strings (not really sure if this will solve correctly my issue and how this can be implemented)?

I don't think jackson can provide a straightforward actual solution for your use-case but I can suggest a different solution.
If you can afford to have a wrapper object around these Strings, let's say LinkWrapper, you can add another serializer of LinkWrapper which will just delegate to your current serializer. In your LinkWrapper object you will annotate the String with your annotation and so your delegated serializer will serialize it unencoded.
Also your output won't be that of an object since your custom serializer only delegates to that of a string serializer.
If you need a code sample just say but I think it's a really simple case of composition.

Related

How to make custom request mapping annotation with spring to add prefixes?

Using spring boot 2 on java 11, I want to make a custom annotation for each REST API version (eg: "/api/v1/") that can be joined with subsequent URI components as below:
#APIv1("/users/") // this annotation should prepend "/api/v1/{argument}"
public class UserController {
#GetMapping("/info")
public String info() {return "This should be returned at /api/v1/users/info/";}
/* More methods with mappings */
}
The problem is I don't know how to define that #APIv1 annotation.
From what I've searched, I referenced https://stackoverflow.com/a/51182494/ to write the following:
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#RestController
#RequestMapping("/api/v1")
#interface APIv1 {
#AliasFor(annotation = RestController.class)
String value() default "";
}
But this cannot handle arguments. Doing the same as above will route to /api/v1/info/ whether the argument is given or not. It's better than nothing since I can switch the method annotation to #GetMapping("/users/info"), but I was wondering if there was a way to combine the constant with an argument to reduce repetition across method annotations within the same controller class.
In #APIv1 you defined:
#RequestMapping("/api/v1")
So it is working as you told it to.

Custom handler for response body in Jackson / Spring

I am trying to intercept the object that is being returned in my controller so that I can create a flat JSON structure of the response, before Spring invokes Jackson's serialization process.
I am going to support a query parameter that allows the client to flatten the response body. Something like:
/v1/rest/employees/{employeId}/id?flat=true
The controller method looks something like:
public Employee getEmployee(...) {}
I would like to avoid implementing this flattening logic in every one of my service calls and continue to return the Employee object.
Is there some kind of facility in Spring that would allow me to A) read the query string and B) intercept the object that is being returned as the response body?
Here's one idea. There may be a better way, but this will work:
Define an extra request mapping to do the flat mapping:
#RequestMapping(path = "/endpoint", params = {"flat"})
public String getFlatThing() {
return flatMapper.writeValueAsString(getThing());
}
// The Jackson converter will do its ordinary serialization here.
#RequestMapping(path = "/endpoint")
public Thing getFlatThing() {
return new Thing();
}
the "flatMapper" implementation can be whatever you like so long as it works.
One option is to use Jackson's ObjectMapper to write the value as json first and then use https://github.com/wnameless/json-flattener to flatten that to your desired output. There may also be a way to define a custom ObjectMapper that does flat mapping, though that would take some more work on your part.

Separate validation annotation and it's validator in separate modules

Situation: you have module with DTO objects used in your API, so that other project(s) can reuse then when sending requests. These DTO classes does have bean-validation annotations in them. And you would like to use your custom validations to validate DTO "arriving" via requests. The sender typically does not validate outgoing data, IIUC, and might not be interested in importing validators along with annotations.
Problem(?): bean-validation is defined in a way, where annotation defines who implements it (which is incorrect and it should be otherwise around imo), with possibility to specify empty array as annotation validator (seems like hack) and then pairing is done via manual hashmap manipulations instead of stuff like service loader etc.
How do you do this?
Would you split annotation and it's validator in separate modules?
How would you bind them together? I think it should be possible to use {} as validator and then use org.hibernate.validator.internal.metadata.core.ConstraintHelper#putValidatorDescriptors to bind them together, but I did not test it yet + maybe there is better way...
I agree that the annotation defining the validator does feel backwards. While not ideal, I've been able to work around this by separating my custom ConstraintValidator into an interface and implementation.
Example:
In api module define constraint and interface validator
#Constraint(validatedBy = MyConstraintValidator.class)
public #interface MyConstraint
{
}
public interface MyConstraintValidator
extends ConstraintValidator<MyConstraint, String>
{
}
In your service module define the implementation
public class MyConstraintValidatorImpl implements MyConstraintValidator
{
private FooService foo;
#Override
public boolean isValid( String value, ConstraintValidatorContext ctx)
{
// Implement your constraint logic
}
}
I we need to separate interface class and validator implementation into separate modules, it's possible. And even in a way, which I said in original question, that should be used. In API module you declare validation for example as:
#Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
#Retention(RUNTIME)
#Documented
#Constraint(validatedBy = {})
#SupportedValidationTarget(ValidationTarget.ANNOTATED_ELEMENT)
#ReportAsSingleViolation
public #interface AnyUuid {
//...
notice validatedBy = {}. The validator implementation looks like:
public class AnyUuidValidator implements ConstraintValidator<AnyUuid, Object> {
//...
and pairing can be setup using service loader(see javadoc if you don't know how that works). Put into file META-INF/services/javax.validation.ConstraintValidator FQDN of AnyUuidValidator shown above. And all other custom validators. And that should be it.
There is a bug I found with this. If I'm not mistaken. If you have DTO you cannot change (~annotate with constraints) and still want to validate them via bean validation, you can register validation definitions via xml file. If you are doing so and use service loader for pairing definition and implementation of custom validators, there is probably some bug and your custom validators won't be found. So verify this scenario before relying on service loader. But maybe I'm wrong, for me it was feasible to drop this validation trivially so I did to save some time and could ignore this.

Change the return type of annotation method

given the following custom annotation
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface TGT {
String age();
}
and somewhere in a class code it is used for a field as :
#TGT(age="someValue")
private String someID;
Is it possible to change the return value of someID's method age() from "someValue": to so something else or in other words other String value?So when someone gets someID's annotation via reflection and then invokes the age() method of the annotation to get different value?
Yes, it is possible but not with pure reflection. If you want to change the object without replacing it by another object you'll have to use a bytecode manipulation lib like ASM (see how it is done in Lombok). But this way would be very complex.
In case if you are allowed to replace an abject by a proxy it would even be possible with pure reflection + aspects engine (se how Spring does it with help of CGLIB).

How to validate a single parameter in DropWizard

I want to validate some input parameters in DropWizard as documented here: https://dropwizard.github.io/dropwizard/manual/core.html#validation
This example validates an object's properties, which works fine, but my service doesn't receive an object, but the parameters directly. This is a simplification of my code:
#GET
#Path("/MyResource/{myresourceId}")
public MyResource detail(#PathParam("myresourceId") #Valid #Size(min = 2, max = 5) String myresourceId) {
The #Valid and #Size annotations seem to be ignored in this case.
Anyone knows if this is possible and if so how to do it right? Thanks.
It's not possible with those annotations as method parameters. Have a look at this thread.
You might be able to write some AOP code to support this (depending on how you're initializing your resources).

Categories