How to add matchOnUriPrefix on existing Camel Jetty REST routes? - java

We have existing REST routes working with Camel 2.23.1 and jetty. We redirect incoming calls to an appropriate server based on the uri, query, and the user's authentication. We want to handle this more generally.
How can we modify the following code to handle any uri with "/say" as the prefix?
In our RouteBuilder:
RestConfigurationDefinition rConfig = restConfiguration()
.component("jetty")
.port(webserverPort)
.contextPath("/")
.bindingMode(RestBindingMode.off)
.enableCORS(true)
.dataFormatProperty("prettyPrint", "true");
rest("/say")
.get().to("direct:test");
from("direct:test")
.bean(RouteRest.class, "getTestURI(*,*)")
.to("mock:output");
We have tried adding a property to the restConfiguration, ala
.componentProperty("matchOnUriPrefix", "true");
We have tried adding the same property to the rest route definition, ala
rest("/bye?matchOnUriPrefix=true")
We have tried creating a new from statement, which seems to break everything, ala
from("jetty://0.0.0.0:8123/now?matchOnUriPrefix=true").to("direct:test");
I am aware of this question and answer, but don't know how to apply it to my case:
stackoverflow.com/questions/39341784
Further, is it possible to match some incoming calls with explicitly defined uri's, like "/admin/status", and all other uri's to "direct:test"?

We ended up taking out the restConfiguration() entirely and configuring endpoints individually, which fit our expanding requirements anyway. Our oritinal restConfiguration() was limiting the messages that could get to the endpoints themselves. Perhaps we could have modified the restConfiguration directly to enable greater flexibility, including removal of .contextPath("/"). This directly allowed the following code to work:
from("jetty:http://{{ip}}:{{port}}?matchOnUriPrefix=true")
.bean(RestForward.class, "checkUserAuth(*)")
.bean(RestForward.class, "checkDevice(*)")
.bean(RestForward.class, "forward(*,*)")
.to("mock:output");

Related

Programmatically created Feign client and Eureka target

I have started a few days ago to learn about fault tolerance solutions in microservices. I have some microservices in my ecosystem and they are now interconnected with Eureka service lookup. I used FeignClient to call from one to another. As I heard and read, that Hystrix is getting into maintenance, I wondered if I could use Resilience4J in Feign instead of Hystrix. Well, at least not from annotation level right now as it seems. I found a great Feign.Builder adapter to add resilience4j fault tolerance features above a FeignClient as a decorator (https://github.com/resilience4j/resilience4j/tree/master/resilience4j-feign) so I wanted to use it.
So I used this, added together the features and added the default encoder, decoder, etc. items into the feign builder. Turns out I have to finish of course my code with a .target call which creates my client proxy and I could not really do this with Eureka in a good way:
The first constructor, which takes the class type and the URL is hardcoded, so if I add an eureka next server query into this parameter, it is just a hardcoded url for one of the instances, this is not load balanced. Some kinda workaround could be that I create prototype-scope or similar short lived scoped beans of this client and always get the "next url" for the call. This adds lots of burden to use the clients in every class I make. At least as I saw it. Maybe I could add some kind of singleton helper bean around the prototyping, but again this is not a good design as I see
I thought maybe I could create an EurekaTarget from the Target interface, but of course none of the methods indicate any "end of lifecycle" things, not even the apply method. I thought maybe that is one point which is called before doing a service call, but I saw multiple calls towards it so I had to change the url for all calls.
Do you know any better solution to do this migration?
I guess you are using Spring Boot?
The next version v1.0.0 of Resilience4j will support the #FeignClient annotation.
There was a PR which added the functionality -> https://github.com/resilience4j/resilience4j/pull/579
You can then use it as follows:
#FeignClient(name = DUMMY_FEIGN_CLIENT_NAME)
#CircuitBreaker(name = DUMMY_FEIGN_CLIENT_NAME)
public interface DummyFeignClient {
String DUMMY_FEIGN_CLIENT_NAME = "dummyFeignClient";
#GetMapping(path = "/api/{param}")
void doSomething(#PathVariable(name = "param") String param);
}

Spring MVC: Domain restriction for a single controller

I know that we can use Spring security to restrict access to a controller by IP address or even I can create my custom annotation to add some custom logic for this purpose.
I need to restrict access to a controller in my application to only specific domain name ( a third party ), I checked with this third party provider for the IP ranges which I can use to configure at my end, however they want to have more freedom around IP address and would like me to create access based on the domain name.
I checked through the doc but unable to find any such use cases, Can any one help me to understand if this is possible or I need to go back to IP based access mechanism ?
i think you can use Spring's CORS support for this.
ie if the domain that you expect the request is example.com you can have in your controller method the following annotation
#CrossOrigin(origins = "http://example.com")
This #CrossOrigin annotation enables cross-origin requests only for
this specific method. By default, its allows all origins, all headers,
the HTTP methods specified in the #RequestMapping annotation and a
maxAge of 30 minutes is used. You can customize this behavior by
specifying the value of one of the annotation attributes: origins,
methods, allowedHeaders, exposedHeaders, allowCredentials or maxAge.
In this example, we only allow http://localhost:8080 to send
cross-origin requests.
Have a look in the following url https://spring.io/guides/gs/rest-service-cors/

Play framework route parameter authorization

I have REST api on my page and for authentication I use the Play session.
Problem is with authorization, I have tens of endpoints looking like this:
GET /api/domains/:domainId/properties/:propertyId/reports
I could add and if statement on each controller method to check whether user has permissions to that domain or property, but can I handle it somehow globally?
I found this module, but it does not seem to handle parameters, just checks if user is in some group/role or not. https://www.playframework.com/documentation/1.0.2.1/secure
I solved this using a custom RequestHandler. There you can extract parameters from the path and validate them. (In scala I could even modify the request route in order to avoid repeating these parameters in all routes, I don't know if you can do it in java too).
(See:
https://www.playframework.com/documentation/2.4.x/JavaHttpRequestHandlers)
You can use the Security.Authenticated annotation as detailed here. For more specific permissions, I recommend Deadbolt

CorsFilter setAllowedOrigins(*) redundancy

Is there any reason to write
corsFilter.setAllowedOrigins(new HashSet<String>(Arrays.asList("*")));
where the definition of allowedOrigins in the Restlet framework is
private Set<String> allowedOrigins = SetUtils.newHashSet("*");
Another question - when I write the above line, I get an error running my app.
For some reason I get duplicate origin, and the client refuses to accept it - in the request I can see "*" and the domain name where I sent the request from.
How does this duplication can happen, and what is the best way to deal with it?
You're right, there is no need to provide this value as it is already the default one. Could you tell me where you read that such value must be set?
I don't understand what really happens with the second part of your question, as I'm not able to reproduce it (with CorsFilter, or CorsService).
Could you try using the CorsService instead? This service helps to configure the Cors feature, and is integrated in the list of services either of the Application, or the Component, for example in the constructor of the application:
public TestCorsApplication() {
CorsService corsService = new CorsService();
corsService.setAllowedCredentials(true);
corsService.setSkippingResourceForCorsOptions(true);
getServices().add(corsService);
}

How do you unit test a servlet endpoint in apache camel?

I'm new to Camel and now have a simple route running in my Tomcat server. The route is built like this:
Processor generateWebResponse = new MySpecialProcessor();
from("servlet:///url?matchOnUriPrefix=true").process(generateWebResponse);
I tried a simple unit test like this:
Exchange lAuthRequest = createExchangeWithBody("[json body!]");
template.send("servlet:///url", lAuthRequest);
assertEquals("baseline body", lAuthRequest.getOut().getBody());
but get an exception indicating that I can't make a servlet endpoint. Here is the exception message:
org.apache.camel.FailedToCreateProducerException: Failed to create Producer for endpoint: Endpoint[servlet:///url]. Reason: java.lang.UnsupportedOperationException: You cannot create producer with servlet endpoint, please consider to use http or http4 endpoint.
This is new development so I don't have many constraints other than good design. I'm open to suggestions that require changes to the route. Also, if I'm doing something above that isn't idiomatic, I'm happy to revise the question with any suggested improvements.
You need to use a http client component to send a message to Tomcat, eg for example the camel--http component: http://camel.apache.org/http
You would then need to know the port number Tomcat runs the servlet at, eg
template.send("http://localhost:8080/myapp/myserver", lAuthRequest);
You would need to add camel-http to your classpath, eg if you use maven then add it as a dependency.
I solved my problem by breaking the route into two parts. Now the route declaration looks like this:
from("servlet:///auth?matchOnUriPrefix=true").inOut("direct:auth");
from("direct:auth").process(new AuthorizationProcessor());
And the test looks like this:
Exchange lAuthRequest = createExchangeWithBody("test body");
template.send("direct:auth", lAuthRequest);
assertEquals("processed body", lAuthRequest.getOut().getBody());
This isn't a complete test, but allows me to get coverage of all of the route excluding the incoming servlet part. I think it's sufficient for the time being.

Categories

Resources