Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Data JPA Invalid page.sort Parameters

In a web application that uses Spring Data JPA with Hibernate, we utilize the web pagination functionality to provide paging and sorting capabilities in various lists of entities.

@Controller
public class MyEntityController {
   @RequestMapping(method = RequestMethod.GET)
   public ModelAndView list(Pageable pageable) { ... }
}

@Configuration
public class MyWebMvcConfig extends WebMvcConfigurationSupport {
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        super.addArgumentResolvers(argumentResolvers);
        argumentResolvers.add(new PageableArgumentResolver());
    }
}

public interface MyEntityRepository extends PagingAndSortingRepository<MyEntity, String> {
    Page<MyEntity> findByPropertyX(String propertyX, Pageable pagable);
}

This allows for entity properties to be defined in the rendered html as special sort request parameters, where the page.sort value actually matches a property in the entity upon which to sort.

<table>
    <thead>
        <tr>
            <th><a href="?page.sort=propertyX&amp;page.sort.dir=asc">Property X</a></th>
            <th><a href="?page.sort=propertyY&amp;page.sort.dir=asc">Property Y</a></th>
        </tr>
    </thead>
    <tbody>...</tbody>
</table>

This produces a resulting URL such as:

http://host/context-root/entities/?page.sort=propertyX&page.sort.dir=asc

The problem is that users may modify the URL to use invalid page.sort properties that either reference non-existent column/property names, or worse, that use invalid JPA query characters that result in invalid syntax.

For example, if the URL is modified to sort on "noSuchProperty":

http://host/context-root/entities/?page.sort=noSuchProperty&page.sort.dir=asc

But this property doesn't exist, the following exception will be thrown:

java.lang.IllegalArgumentException: No property noSuchProperty found for type class com.my.company.MyEntity
    at org.springframework.data.repository.query.parser.Property.<init>(Property.java:76)
     . . .
    at org.springframework.data.repository.query.parser.AbstractQueryCreator.createQuery(AbstractQueryCreator.java:86)
     . . .
    at $Proxy68.findByPropertyX(Unknown Source)
    at com.my.company.MyEntityRepository.findByPropertyX(MyEntityRepository.java:17

Likewise, if the URL is modified to an invalid query syntax character, such as """:

http://host/context-root/entities/?page.sort=%22&page.sort.dir=asc

The following error will occur:

java.lang.StackOverflowError
    java.util.regex.Pattern$GroupTail.match(Pattern.java:4227)
    . . .
    org.springframework.data.repository.query.parser.Property.create(Property.java:326)
    org.springframework.data.repository.query.parser.Property.create(Property.java:326)
    org.springframework.data.repository.query.parser.Property.create(Property.java:326)
    org.springframework.data.repository.query.parser.Property.create(Property.java:326)

(There is also a third flavor of exceptions which results in a org.hibernate.QueryException when the @Query is explicitly defined on the Repository method.)

Spring Data JPA abstracts away the details of the sorting, paging, and handling of these parameters; however, it doesn't seem to gracefully handle these scenarios (i.e. where an invalid sort parameter is specified).

We could add in some additional custom logic to validate that the sort property actually exists on the entity; however, I'm wondering if there is a cleaner more centralized approach for doing this such that we don't lose the benefits and simplicity of the Spring Data JPA abstractions. We use this sorting capability throughout our app with many different entities, so ideally, we'd want more of a generic approach, rather than having to explicitly define or check the sort properties for every entity page requested.

Specifically, we actually extend the PageableArgumentResolver to accept an annotated sort default value that is provided in our controller (not illustrated in the code examples for simplicity), so we'd like to just fallback to this default sort order, or just the default sorting order for the entity, rather than throwing an exception.

Some ideas and attempts.. I could use a QueryCreationListener to intercept the query creation and get the sort parameter; however, I can't actually modify the query at that point. Or, I can extend and use a custom PageableArgumentResolver (we are already doing this) to grab the sort parameters; however, I don't have access to the entity at that point, nor the ability to determine if the entity actually has a property by that name. We could declare the supported properties explicitly; however, again, this defeats the idea of centrally and automatically handling this scenario without requiring specific or declared knowledge of the entities.

Is there any other type of interceptor or similar construct that I can utilize to centrally validate pageable sort parameters and modify if necessary before invoking the query? Or is there any type of configuration or way that Spring can automatically handle this scenario such that it more gracefully handles invalid sort params?

like image 245
shelley Avatar asked Jun 27 '12 17:06

shelley


1 Answers

I was taking a look at the code and I think some more of the stack trace would be helpful. But from what I can see, I think there are two places you might want to tackle if you are in the mood to rewrite some Spring code.

There are two scenarios here, in the first one you are passing a sort field that doesn't exist in the object/table. What you really want is for that bad parameter to be silently ignored all the time, not just when passing in a 1PageableArgumentResolver]1. I'm thinking it should be an option on the AbstractQueryCreator (and hence, the JpaQueryCreator) to ignore bad parameters on a sort.

The second part that should be tackled is probably the PageableArgumentResolver. If you pass empty strings or something that doesn't make sense like %20 then it should ignore that parameter and not send it through to the PageRequest.

Happy hacking and good luck. Reading your post has made me realize that my site is vulnerable to the same problem and I really have no good solution.

like image 137
sbzoom Avatar answered Nov 14 '22 23:11

sbzoom