Spring Rest API - spurious/not requested parameters strategy - java

According to this discussion - "RESTful API - Correct behavior when spurious/not requested parameters are passed in the request", we shouldn't ignore not requested parameters but how we can process this situation on all endpoint?
For example for this endpoint:
#RequestMapping(value = "/transactions/",
method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public RestResultSupport getCommandsById(#PathVariable("id") String id) throws IOException {
validateId(id);
....
return result;
}
We'll get the same result for 2 different requests:
curl localhost:8080/?id=1200
and
curl localhost:8080/?id=1200&unknown=incorrect
If we imagine that we should process this situation on 20 endpoints, how can we simplify our code? Does Spring provide some tools for that?

I found only one way to do this - implement HandlerInterceptor.
Please have a look at an example:
public class RequestInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
Set<String> innerParams = request.getParameterMap().keySet();
Set<String> describedParams = new HashSet<>();
for (MethodParameter methodParameter : ((HandlerMethod) handler).getMethodParameters()) {
if (methodParameter.hasParameterAnnotation(RequestParam.class)) {
RequestParam requestParam = methodParameter.getParameterAnnotation(RequestParam.class);
describedParams.add(requestParam.name());
}
}
for (String inputRequestParam : innerParams) {
if (!describedParams.contains(inputRequestParam)) {
throw new BadDataException("Please provide valid request paramaters. [ Valid request parameters - " + describedParams + " ]");
}
}
return true;
}
... empty other required methods ...
}
Code analyzes required parameters and if it gets something unknown it will throw RuntimeException

Related

How to return 404 not found in json format using restful api in java?

i would like to implement an exception handler for restful api if the uri is not matched.
For example: url is
localhost:8080\test\generateNumber will return
{"response_code":"200"}
andif the url is wrong for example:
localhost:8080\test\generateNumber2 will return
{"response_code":"404","message":"uri not found"}
i have no idea on how to do it. Can someone help?
I presume you're using Spring?
In that case you can use #ExceptionHandler like this:
#RestController
public class Example1Controller {
#GetMapping(value = "/testExceptionHandler", produces = APPLICATION_JSON_VALUE)
public Response testExceptionHandler(#RequestParam(required = false, defaultValue = "false") boolean exception)
throws BusinessException {
if (exception) {
throw new BusinessException("BusinessException in testExceptionHandler");
}
return new Response("OK");
}
#ExceptionHandler(BusinessException.class)
public Response handleException(BusinessException e) {
return new Response(e.getMessage());
}
}
And get a message in response.
More - in this manual.

Is there a way to get a list of all jersey urls a user, based on their role and #RolesAllowed, has access to?

I am hoping to offer a rest api call to clients(via plain jersey, not spring) to return a list of all endpoints allowed for the specific user based on the JWT they send in the header. I have found on stackoverflow(thanks contributors!) example code to get all endpoints, regardless of role, but not the subset based on role. I have found how to get the annotations per method as well, but would like to avoid re-inventing the wheel of "if #PermitAll and not #DenyAll, or role in RolesAllowed, etc...".
Any chance Jersey 2.0 has a a method I can call that will resolve to true/false given SecurityContext and url endpoint or method?
boolean allowed = isMethodAllowed(SecurityContext ctx, String url);
Or
boolean allowed = isMethodAllowed(SecurityContext ctx, Class method);
Thanks to this post: Listing all deployed rest endpoints (spring-boot, jersey)
Specifically Johanne Jander's post(thank you!), I've come up with the below code which seems to work in my case, a simple jersey use case. Providing it here in case it's useful to others.
#Path("/v1/userinterface")
#Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public class MyUI extends MyRestApi {
private final static Logger log = LoggerFactory.getLogger(MyUI.class);
#Context
private Configuration configuration;
#Context
private SecurityContext security;
#Path("/allowedendpoints")
#GET
#Operation(summary = "List API access points allowed for the currently authenticated user", tags = {
"ui" }, description = "Returns a list of urls", responses = {})
public Response showAll(#Context UriInfo ui) {
log.debug("Get list of all allowed endpoints for user: " + security.getUserPrincipal().getName());
HashMap<String, ArrayList<String>> map = new HashMap<String, ArrayList<String>>();
for (Class<?> c : configuration.getClasses()) {
// Since all of my endpoint classes extend MyRestApi,
// only scan them, not all classes
if (MyRestApi.class.isAssignableFrom(c)) {
scanClass(c, map);
}
}
return Response.ok().entity(map).build();
}
public void scanClass(Class<?> baseClass, HashMap<String, ArrayList<String>> map) {
Builder builder = Resource.builder(baseClass);
if (null != builder) {
Resource resource = builder.build();
String uriPrefix = "";
process(uriPrefix, resource, map);
}
}
private void process(String uriPrefix, Resource resource, HashMap<String, ArrayList<String>> map) {
// recursive method
String pathPrefix = uriPrefix;
List<Resource> resources = new ArrayList<>();
resources.addAll(resource.getChildResources());
if (resource.getPath() != null) {
pathPrefix = (pathPrefix + "/" + resource.getPath()).replaceAll("//", "/");
}
for (ResourceMethod method : resource.getAllMethods()) {
if (method.getType().equals(ResourceMethod.JaxrsType.SUB_RESOURCE_LOCATOR)) {
resources.add(Resource
.from(resource.getResourceLocator().getInvocable().getDefinitionMethod().getReturnType()));
} else {
if (isPathAllowed(security, method.getInvocable().getDefinitionMethod())) {
if (map.containsKey(pathPrefix))
map.get(pathPrefix).add(method.getHttpMethod());
else
map.put(pathPrefix, new ArrayList<String>(Arrays.asList(method.getHttpMethod())));
}
}
}
for (Resource childResource : resources) {
process(pathPrefix, childResource, map);
}
}
public boolean isPathAllowed(SecurityContext ctx, Method method) {
// #DenyAll on the method takes precedence over #RolesAllowed and #PermitAll
if (method.isAnnotationPresent(DenyAll.class)) {
return (false);
}
// #RolesAllowed on the method takes precedence over #PermitAll
RolesAllowed rolesAllowed = method.getAnnotation(RolesAllowed.class);
if (rolesAllowed != null) {
return (hasRole(ctx, rolesAllowed.value()));
}
// #PermitAll on the method takes precedence over #RolesAllowed on the class
if (method.isAnnotationPresent(PermitAll.class)) {
return (true);
}
// #DenyAll can't be attached to classes
// #RolesAllowed on the class takes precedence over #PermitAll on the class
rolesAllowed = method.getDeclaringClass().getAnnotation(RolesAllowed.class);
if (rolesAllowed != null) {
return (hasRole(ctx, rolesAllowed.value()));
}
// #PermitAll on the class
if (method.getDeclaringClass().isAnnotationPresent(PermitAll.class)) {
return (true);
}
return (false); // default
}
private boolean hasRole(SecurityContext ctx, String[] rolesAllowed) {
for (final String role : rolesAllowed) {
if (ctx.isUserInRole(role)) {
return (true);
}
}
return (false);
}
Which returns endpoints the currently authenticated user has access to based on SecurityContext markup with the #DenyAll, #PermitAll and #RolesAllowed annotations.
Full disclosure, my app is simple with just basic class and method annotations at the endpoints. ymmv.
Sample output:
{
"/v1/resources" : [
"POST",
"GET"
],
"/v1/resources/{id}" : [
"DELETE",
"GET"
]
}

Authorization in JAX-RS

I am developing an application using javaEE / Wildfly and JAX-RS for the restful service.
I have this kind of endpoint :
#POST
#Path("/add")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response addSchool(SchoolDto schoolDto, #HeaderParam("token") String userToken) {
List<String> actionsNeeded = new ArrayList<String>(
Arrays.asList(
"create school"
));
if (authService.userHasActionList(userToken, actionsNeeded) == false )
{
return authService.returnResponse(401);
}
Response addSchoolServiceResponse = schoolResponse.create(schoolDto);
return addSchoolServiceResponse;
}
Using the token in Header my auth service will check if the user account has, in his list of authorized actions, those that are necessary to use the checkpoint.
It's working, but I'm repeating that on each checkpoint ... I'm looking for a way to do that :
#POST
#Path("/add")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
#Annotation("action 1 needed", "Action 2 needed")
public Response addSchool(SchoolDto schoolDto, #HeaderParam("token") String userToken) {
Response addSchoolServiceResponse = schoolResponse.create(schoolDto);
return addSchoolServiceResponse;
}
an annotation where i can pass some parameters (my actions and most important be able to have the user token) who trigger using filter or whatever the security check return a 401 or let the method to be executed if user is allowed to be there.
I've find a lot of stuff (#Secured etc...) for security based on role but not on action like that
Is someone already did something like that ?
Finally I've started all over and it's working, my principal problem was to access token in the header and working with annotations and it's ok now (just need to insist and try one more time i assume ...) here is what it's look likes :
#Provider
#Actions
public class AuthorizationFilter implements ContainerRequestFilter {
#EJB
AuthService authService;
#Context
private ResourceInfo resourceInfo;
List<String> actionsNeeded = new ArrayList<String>();
#Override
public void filter(ContainerRequestContext reqContext) throws IOException {
Actions annotations = resourceInfo.getResourceMethod().getAnnotation(Actions.class);
String token;
try {
token = reqContext.getHeaders().get("token").get(0);
for (String annotation : annotations.value()) {
actionsNeeded.add(annotation);
}
if (authService.userHasActionList(token, actionsNeeded) == false )
{
reqContext.abortWith(authService.returnResponse(401));
return;
}
} catch (Exception e) {
System.out.println("Headers 'token' does not exist !");
reqContext.abortWith(authService.returnResponse(400));
}
}
}

How do you allow 400 Errors to propagate when using Feign with Hystrix?

I'm building a SpringBoot microservice that calls another microservice and naturally want to use Hystrix and Feign clients, which are both included with Spring Cloud. I'm using version Camden.SR5.
For any timeouts, connection failures and 50x response codes from Feign, I want Hystrix to kick in and work as normal: tripping the circuit breaker and calling the fallback (if configured), etc. It does this by default, so I'm good.
But for 40x response codes, which include things like invalid entry, the wrong format of fields etc, I want Hystrix to propagate these exceptions to the caller, so I can handle them as I choose too. This isn't the default I've observed. How do you configure Hystrix/Feign to do this in Spring Cloud?
Out of the box using the following code:
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.hateoas.Resource;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
#FeignClient(name = "dog-service", url = "http://...")
public interface DogsFeignClient {
#RequestMapping(method = RequestMethod.POST, path = "/dogs")
Resource<Dog> createDog(Dog dog);
}
Generates this exception, which doesn't lend itself to nicely passing that 40x response back to the caller:
com.netflix.hystrix.exception.HystrixRuntimeException: DogsFeignClient#createDog(Dog) failed and no fallback available.
at com.netflix.hystrix.AbstractCommand$22.call(AbstractCommand.java:805) ~[hystrix-core-1.5.6.jar:1.5.6]
....lines ommited for brevity....
Caused by: feign.FeignException: status 400 reading DogsFeignClient#createDog(Dog); content:
{
"errors" : [ {
"entity" : "Dog",
"property" : "numberOfLegs",
"invalidValue" : "3",
"message" : "All dogs must have 4 legs"
} ]
}
at feign.FeignException.errorStatus(FeignException.java:62) ~[feign-core-9.3.1.jar:na]
at feign.codec.ErrorDecoder$Default.decode(ErrorDecoder.java:91) ~[feign-core-9.3.1.jar:na]
at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:138) ~[feign-core-9.3.1.jar:na]
at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:76) ~[feign-core-9.3.1.jar:na]
at feign.hystrix.HystrixInvocationHandler$1.run(HystrixInvocationHandler.java:108) ~[feign-hystrix-9.3.1.jar:na]
at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:301) ~[hystrix-core-1.5.6.jar:1.5.6]
at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:297) ~[hystrix-core-1.5.6.jar:1.5.6]
at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:46) ~[rxjava-1.1.10.jar:1.1.10]
... 26 common frames omitted
I can of course look at the com.netflix.hystrix.exception.HystrixRuntimeException, cause field which contains a feign.FeignException and buried in the description is the JSON response itself, with line breaks and such. But the cause field of feign.FeignException is a reference to itself. Is there a way to get a deeper exception propagated instead of the HystrixRuntimeException?
Also is there a way to get the raw body included with the response from the downstream service, so I don't have to deconstruct the message field of the nested exception?
This can be achieved using a separate configuration, which will wrap 400's in a subclass of HystrixBadRequestException and throw them to the client code.
These exceptions don't affect the circuit breaker state - if the circuit is closed, it will remain closed, and if it's open, it will remain open.
#FeignClient(name = "dog-service",
url = "http://...",
configuration=FeignPropagateBadRequestsConfiguration.class)
public interface DogsFeignClient {
#RequestMapping(method = RequestMethod.POST, path = "/dogs")
Resource<Dog> createDog(Dog dog);
}
where FeignPropagateBadRequestsConfiguration is
#Configuration
public class FeignSkipBadRequestsConfiguration {
#Bean
public ErrorDecoder errorDecoder() {
return (methodKey, response) -> {
int status = response.status();
if (status == 400) {
String body = "Bad request";
try {
body = IOUtils.toString(response.body().asReader());
} catch (Exception ignored) {}
HttpHeaders httpHeaders = new HttpHeaders();
response.headers().forEach((k, v) -> httpHeaders.add("feign-" + k, StringUtils.join(v,",")));
return new FeignBadResponseWrapper(status, httpHeaders, body);
}
else {
return new RuntimeException("Response Code " + status);
}
};
}
}
and FeignBadResponseWrapper is
#Getter
#Setter
public class FeignBadResponseWrapper extends HystrixBadRequestException {
private final int status;
private final HttpHeaders headers;
private final String body;
public FeignBadResponseWrapper(int status, HttpHeaders headers, String body) {
super("Bad request");
this.status = status;
this.headers = headers;
this.body = body;
}
}
This is a bit of a hack, and you can get the response body only in ErrorDecoder, because after that the stream will be closed. But using this, you can throw the response data to client code without affecting the circuit:
try {
return dogsFeignClient.createDog(dog);
} catch (HystrixBadRequestException he) {
if (he instanceof FeignBadResponseWrapper) {
// obtain data from wrapper and return it to client
} else {
// return basic error data for other exceptions
}
}

How to mock HttpServletRequest with Headers?

I am using Mockito with JUnit to test an application. I need to add headers to an HttpServletRequest while mocking. This is the first time I am using mock concept to test the application. How can we set headers to the request object while using this mock concept?
Application code
#Produces({ MediaType.APPLICATION_JSON })
#Path("/devices")
public class DvrRestService {
private static final Logger logger = LoggerFactory.getLogger(DvrRestService.class);
private DvrMiddleService dvrMiddleService;
#Inject
public DvrRestService(DvrMiddleService dvrMiddleService) {
this.dvrMiddleService = dvrMiddleService;
}
#GET
#Path("/{deviceId}/metadata")
public Response getDeviceMetadata(#Context HttpServletRequest request, #PathParam("deviceId") String deviceId,
#RequiredSession final Session session) {
try {
public static String[] REQUEST_HEADERS = { "if-none-match" };
List<String> requiredHeaders = Lists.newArrayList(REQUEST_HEADERS);
Map<String, String> headers = new HashMap<String, String>();
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) { // here gettting NullPointerException
String headerName = headerNames.nextElement();
if (requiredHeaders.contains(headerName.toLowerCase())) {
String value = request.getHeader(headerName);
if (value != null) {
headers.put(headerName, value);
System.out.println("headerName: " + headerName + ", Value: " + value);
}
}
}
DvrResponse response = dvrMiddleService.getDeviceMetadata(deviceId.toUpperCase(), getHeaders(request));
return processResponse(response.statusCode, response.getResponse(), DeviceMetadataResponse.class,
response.getHeaders());
} catch (Exception e) {
return processErrorResponse(e, new DeviceMetadataResponse(), logger);
}
}
}
Test
public class DvrRestServiceTest {
static DvrMiddleService dms;
static HttpServletRequest request;
static Session session;
static DvrRestService drs;
public static final String DeviceId = "000004D42070";
#BeforeClass
public static void init(){
dms = mock(DvrMiddleService.class);
request = mock(HttpServletRequest.class);
session = mock(Session.class);
drs = new DvrRestService(dms);
}
#Test
public void getDeviceMetadataTest(){
Response rs = drs.getDeviceMetadata(request, DeviceId, session);
assertEquals(Response.Status.OK, rs.getStatus());
}
}
As a starting point and demonstration for the principal you can start with the following snippet.
// define the headers you want to be returned
Map<String, String> headers = new HashMap<>();
headers.put(null, "HTTP/1.1 200 OK");
headers.put("Content-Type", "text/html");
// create an Enumeration over the header keys
Enumeration<String> headerNames = Collections.enumeration(headers.keySet());
// mock HttpServletRequest
HttpServletRequest request = mock(HttpServletRequest.class);
// mock the returned value of request.getHeaderNames()
when(request.getHeaderNames()).thenReturn(headerNames);
System.out.println("demonstrate output of request.getHeaderNames()");
while (headerNames.hasMoreElements()) {
System.out.println("header name: " + headerNames.nextElement());
}
// mock the returned value of request.getHeader(String name)
doAnswer(new Answer<String>() {
#Override
public String answer(InvocationOnMock invocation) throws Throwable {
Object[] args = invocation.getArguments();
return headers.get((String) args[0]);
}
}).when(request).getHeader("Content-Type");
System.out.println("demonstrate output of request.getHeader(String name)");
String headerName = "Content-Type";
System.out.printf("header name: [%s] value: [%s]%n",
headerName, request.getHeader(headerName));
}
Output
demonstrate output of request.getHeaderNames()
header name: null
header name: Content-Type
demonstrate output of request.getHeader(String name)
header name: [Content-Type] value: [text/html]
For HttpServletRequest, I would recommend using a fully functional mock type instead of directly mocking it with Mockito mocks. The spring-test library has MockHttpServletRequest for this purpose:
#BeforeClass
public static void init(){
// ...
MockHttpServletRequest mockRequest = new MockHttpServletRequest();
mockRequest.addHeader("Content-Type", "text/html");
mockRequest.addHeader("if-none-match", "*");
mockRequest.addHeader("customHeader", "customValue");
this.request = mockRequest;
}
Rationale
HttpServletRequest is a complex interface with over 20 methods, with well-defined interplay between them. Using a fully functional mock type for HttpServletRequest from a library simplifies the mocking, removing the need to carefully mock out the methods you're using.
One advantage of this approach is that it is more resilient in the face of future refactorings that get the same information using other methods on the class. In the case of retrieving the "if-none-match" header in HttpServletRequest, I see three different methods that could legitimately be used to retrieve the header: getHeader(String name), getHeaders(String name), and getHeaderNames(). Furthermore, the argument for both getHeader and getHeaders are case-insensitive (the same results are returned for "if-none-match", "If-None-Match", etc.), so any possible argument casing would be correct. It's very possible to support this with a direct mock, though it involves extra boilerplate code that complicates the test and makes it less obvious.
The MockHttpServletRequest class from the spring-test library mocks this interface, and allows setting the header and other values via a straightforward API. While the library is designed for testing Spring applications, the MockHttpServletRequest class is independent of any Spring-specific functionality, and should be completely usable on its own even if the application doesn't use Spring.
This worked in my case
mockMvc.perform(post("<<url>>").content("<<jsonStrig>>").header("key", "value"));
can also be used in get request.
I know the OP is using Mockito. This answer is for those using spock. You can accomplish this pretty easily.
class MyTestSpec extends Specification {
HttpServletRequest request = Mock()
MyTestClass myTestClass = new MyTestClass()
def 'my test'() {
setup:
String expectedHeader = "x-mycustom-header"
when:
String someResult = myTestClass.someTestMethod()
then:
// here is where you return your header from the mocked request
1 * request.getHeader(_ as String) >> expectedHeader
}
}

Categories