-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #13514 from gytis/sort-query-validator
Validate REST Data Panache sort query parameter
- Loading branch information
Showing
8 changed files
with
182 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
41 changes: 41 additions & 0 deletions
41
...runtime/src/main/java/io/quarkus/rest/data/panache/runtime/sort/SortQueryParamFilter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package io.quarkus.rest.data.panache.runtime.sort; | ||
|
||
import static javax.ws.rs.core.Response.Status.BAD_REQUEST; | ||
|
||
import java.util.Collections; | ||
|
||
import javax.ws.rs.container.ContainerRequestContext; | ||
import javax.ws.rs.container.ContainerRequestFilter; | ||
import javax.ws.rs.core.MultivaluedMap; | ||
import javax.ws.rs.core.Response; | ||
import javax.ws.rs.ext.Provider; | ||
|
||
@Provider | ||
@SortQueryParamValidator | ||
public class SortQueryParamFilter implements ContainerRequestFilter { | ||
|
||
private static final String SORT_REGEX = "-?([a-z]|[A-Z]|_|\\$|[\u0080-\ufffe])([a-z]|[A-Z]|_|\\$|[0-9]|[\u0080-\ufffe])*"; | ||
|
||
/** | ||
* Verifies that sort query parameters are valid. | ||
* Valid examples: | ||
* * ?sort=name,surname | ||
* * ?sort=$surname&sort=-age | ||
* * ?sort=_id | ||
*/ | ||
@Override | ||
public void filter(ContainerRequestContext requestContext) { | ||
MultivaluedMap<String, String> queryParams = requestContext.getUriInfo().getQueryParameters(); | ||
for (String sort : queryParams.getOrDefault("sort", Collections.emptyList())) { | ||
for (String sortPart : sort.split(",")) { | ||
String trimmed = sortPart.trim(); | ||
if (trimmed.length() > 0 && !trimmed.matches(SORT_REGEX)) { | ||
requestContext.abortWith( | ||
Response.status(BAD_REQUEST) | ||
.entity(String.format("Invalid sort parameter '%s'", sort)) | ||
.build()); | ||
} | ||
} | ||
} | ||
} | ||
} |
14 changes: 14 additions & 0 deletions
14
...time/src/main/java/io/quarkus/rest/data/panache/runtime/sort/SortQueryParamValidator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package io.quarkus.rest.data.panache.runtime.sort; | ||
|
||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
|
||
import javax.ws.rs.NameBinding; | ||
|
||
@NameBinding | ||
@Retention(RetentionPolicy.RUNTIME) | ||
@Target({ ElementType.TYPE, ElementType.METHOD }) | ||
public @interface SortQueryParamValidator { | ||
} |
98 changes: 98 additions & 0 deletions
98
...ime/src/test/java/io/quarkus/rest/data/panache/runtime/sort/SortQueryParamFilterTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
package io.quarkus.rest.data.panache.runtime.sort; | ||
|
||
import static javax.ws.rs.core.Response.Status.BAD_REQUEST; | ||
import static org.mockito.ArgumentMatchers.any; | ||
import static org.mockito.ArgumentMatchers.argThat; | ||
import static org.mockito.BDDMockito.given; | ||
import static org.mockito.Mockito.times; | ||
import static org.mockito.Mockito.verify; | ||
|
||
import javax.ws.rs.container.ContainerRequestContext; | ||
import javax.ws.rs.core.MultivaluedHashMap; | ||
import javax.ws.rs.core.MultivaluedMap; | ||
import javax.ws.rs.core.Response; | ||
import javax.ws.rs.core.UriInfo; | ||
|
||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.extension.ExtendWith; | ||
import org.mockito.ArgumentMatcher; | ||
import org.mockito.Mock; | ||
import org.mockito.junit.jupiter.MockitoExtension; | ||
|
||
@ExtendWith(MockitoExtension.class) | ||
class SortQueryParamFilterTest { | ||
|
||
@Mock | ||
private ContainerRequestContext requestContext; | ||
|
||
@Mock | ||
private UriInfo uriInfo; | ||
|
||
private final SortQueryParamFilter filter = new SortQueryParamFilter(); | ||
|
||
@BeforeEach | ||
void setUp() { | ||
given(requestContext.getUriInfo()).willReturn(uriInfo); | ||
} | ||
|
||
@Test | ||
void shouldAllowValidParameters() { | ||
MultivaluedMap<String, String> map = new MultivaluedHashMap<>(); | ||
map.putSingle("sort", "$name"); | ||
map.putSingle("sort", "-surname_1"); | ||
given(uriInfo.getQueryParameters()).willReturn(map); | ||
|
||
filter.filter(requestContext); | ||
|
||
verify(requestContext, times(0)).abortWith(any()); | ||
} | ||
|
||
@Test | ||
void shouldAllowValidParametersWithMultipleValues() { | ||
MultivaluedMap<String, String> map = new MultivaluedHashMap<>(); | ||
map.putSingle("sort", "$name,-surname_1"); | ||
given(uriInfo.getQueryParameters()).willReturn(map); | ||
|
||
filter.filter(requestContext); | ||
|
||
verify(requestContext, times(0)).abortWith(any()); | ||
} | ||
|
||
@Test | ||
void shouldAllowEmptyParameters() { | ||
given(uriInfo.getQueryParameters()).willReturn(new MultivaluedHashMap<>()); | ||
|
||
filter.filter(requestContext); | ||
|
||
verify(requestContext, times(0)).abortWith(any()); | ||
} | ||
|
||
@Test | ||
void shouldCatchInvalidParameter() { | ||
MultivaluedMap<String, String> map = new MultivaluedHashMap<>(); | ||
map.putSingle("sort", "$name"); | ||
map.putSingle("sort", "(surname_1"); | ||
given(uriInfo.getQueryParameters()).willReturn(map); | ||
|
||
filter.filter(requestContext); | ||
|
||
verify(requestContext).abortWith(argThat(abortResponseMatcher("(surname_1"))); | ||
} | ||
|
||
@Test | ||
void shouldCatchInvalidParameterValue() { | ||
MultivaluedMap<String, String> map = new MultivaluedHashMap<>(); | ||
map.putSingle("sort", "$name,1_surname"); | ||
given(uriInfo.getQueryParameters()).willReturn(map); | ||
|
||
filter.filter(requestContext); | ||
|
||
verify(requestContext).abortWith(argThat(abortResponseMatcher("$name,1_surname"))); | ||
} | ||
|
||
private ArgumentMatcher<Response> abortResponseMatcher(String sortValue) { | ||
return response -> response.getStatus() == BAD_REQUEST.getStatusCode() | ||
&& response.getEntity().equals(String.format("Invalid sort parameter '%s'", sortValue)); | ||
} | ||
} |