Passing WebSocket parameters to a programmatical endpoint - java

My front-end code looks like this:
const ws = new WebSocket("wss://localhost/yeah?param1=value1&param2=value2");
My endpoint at the backend is programmatical:
class YeahEndpoint extends Endpoint {
...
#Override
public void onOpen(Session session, EndpointConfig ec) {
Map<String, String> params = session.getPathParameters(); <-- returns empty map! No param1 or param2.
}
...
}
As I was not able to find relevant information on the web, I need to ask this here: how a programmatical endpoint obtains the request parameters?

I simply should have used session.getQueryString() instead of session.getPathParameters().

If you are using the Jetty as the back-end, look at the following code:
session.getUpgradeRequest().getParameter("param")

Related

Translating from WSO2 to camel with Java DSL: How to forward with URI pattern

I'm removing WSO2 from our stack and I have to write in Camel Java DSL the endpoints that were implemented in WSO2.
In WSO2 we had an endpoint as below:
<resource methods="OPTIONS GET" uri-template="/request/{data}" inSequence="requestreset"/>
<http method="GET" uri-template="http://127.0.0.1/index.php?_q=requestreset&data={uri.var.data}"/>
My code in Java Camel's Router is:
public class DefaultRouteBuilder extends RouteBuilder {
private HashMap<String, String> routeCorresponding = new HashMap();
#Override
public void configure() throws Exception {
routeCorresponding.put("reset/request/{data}", "http://127.0.0.1/index.php?_q=requestreset&data={data}");
for (Map.Entry<String, String> pair : routeCorresponding.entrySet()) {
String url = pair.getKey();
String target = pair.getValue();
String resultTarget = target.contains("?") ? target + "&bridgeEndpoint=true" : target + "?bridgeEndpoint=true";
fromF("servlet:"+ url +"?matchOnUriPrefix=true")
.log("Request: ${in.header."+ Exchange.HTTP_METHOD +"} to ${in.header."+ Exchange.HTTP_URI +"}")
.toF(resultTarget);
}
}
}
But it doesn't work as I would want it because when I make a request to tomcat.myserver.com:8080/camel-example-servlet/reset/request/blablablablabla I get a response this:
org.apache.camel.http.common.HttpOperationFailedException: HTTP operation failed invoking http://127.0.0.1/index.php/reset/request/blablablablabla?_q=requestreset&data=%7Bdata%7D with statusCode: 404
Instead of http://127.0.0.1/index.php/reset/request/blablablablabla?_q=requestreset&data=%7Bdata%7D, I would like the following request to be on http://127.0.0.1/index.php?_q=requestreset&data=blablablablabla
Is it possible to achieve in Camel/Java DSL that? Basically what WSO2 was implementing with the URI template and the curly brackets around fields?
You can absolutely achieve that - but your {data} block is stored as a header, so you need to refer to it as ${header.data} in your target URI.
Here's an example using the REST DSL:
restConfiguration().component("servlet");
rest("/reset/request/{data}")
.get()
.route()
.log("Received request...")
.setHeader(Exchange.HTTP_PATH, simple("/index.php"))
.setHeader(Exchange.HTTP_QUERY, simple("_q=requestreset&data=${header.data}"))
.to("http://localhost:8080?bridgeEndpoint=true");
Edit based on your question below. Alternatively, if you need to proxy hundreds of URLs, instead of creating hundreds of routes, you could just create one single route which proxies them all and implement your routing logic in a Processor, e.g.:
from("servlet:?matchOnUriPrefix=true")
.process(new Processor() {
public void process(Exchange exchange) throws Exception {
// set your target URI here, look it up from the HashMap, etc.
}
})
.to("http://localhost:8080?bridgeEndpoint=true");

Vertx - missing GET parameters while securing route using OAuth2

I followed this example of securing route using OAuth2 with GitHub provider: http://vertx.io/docs/vertx-web/java/#_oauth2authhandler_handler and it works fine, except missing GET parameters after request redirection.
My code:
public class MyVerticle extends AbstractVerticle {
#Override
public void start() throws Exception {
HttpServer server = vertx.createHttpServer();
Router router = Router.router(vertx);
OAuth2Auth authProviderGitHub = GithubAuth.create(vertx, "<CLIENT_ID>", "<CLIENT_SECRET>");
OAuth2AuthHandler oauth2 = OAuth2AuthHandler.create(authProviderGitHub, "http://localhost:8080/callback");
oauth2.setupCallback(router.route());
router.route("/protected/*").handler(oauth2);
Handler<RoutingContext> requestHandler = (routingContext) -> {
String paramValue = routingContext.request().getParam("param");
routingContext.response().end("PARAM: " + paramValue);
};
router.get("/endpoint").handler(requestHandler);
router.get("/protected/endpoint").handler(requestHandler);
server.requestHandler(router::accept).listen(8080);
}
}
I have two simple endpoints:
/endpoint // public, without protection
and
/protected/endpoint // protected with OAuth2
When I call from a browser /endpoint with
http://localhost:8080/endpoint?param=foo
it works as expected and return PARAM: foo, whereas when I call protected endpoint with
http://localhost:8080/protected/endpoint?param=foo
it correctly redirect me to GitHub login page, then return query to my handler but without GET parameters, so response from the endpoint is PARAM: null.
Any idea what I'm doing wrong?
On vert.x <= 3.4.2 only the path was being used for the redirect, the 3.5 series has been improved and can rely on the full uri, so your code will work on that version.

Restlet URI Pattern

I work on 2.1-M7 version of Restlet (I have to update it but this is an another problem)
I use directly Restlet, without any webserver before it Starting a component. Adding some virtualhosts on it.
And in the host I add entrypoints with method attach(string uriPattern, Restlet entrypoint)
My problem is :
When I add with attach the uri "/test" with the entrypoint Test.class (with a method who print : "hello world") with a curl I can call "/testmeagain" and it's work (return "hello world") because it's a pattern?
So I use this : http://docs.oracle.com/javase/1.5.0/docs/api/java/util/regex/Pattern.html?is-external=true
And try "/test$" but in curl "/test" and "/testmeagain" return 404 now
Maybe I miss something?
Thank you if you have any suggestion or response to help me.
In fact, in Restlet, there is a matching mode for routes. Here is the behavior in the framework:
When you attach a route on a virtual, the default mode is "STARTS WITH". So with something like attach("/test", ...), URLs like /test and /testsomething will match.
When you attach a route on a router, the default mode is "EQUALS". So with something like attach("/test", ...), only URL /test will match.
The attach method returns a template route on which you can change this matching:
TemplateRoute route = component.getDefaultHost().attach(
"/test", new Restlet() {
#Override
public void handle(Request request, Response response) {
response.setEntity("test", MediaType.TEXT_PLAIN);
}
});
// Default matching mode
int defaultMatching = route.getMatchingMode();
// Set another matching mode
route.setMatchingMode(Template.MODE_EQUALS);
In fact, it's more usually to implement a Restlet application and attach it to a virtual host on the component. In this case, you will have exact matching.
Here is the way to do:
Component component = new Component();
(...)
MyRestletApplication application = new MyRestletApplication();
component.getDefaultHost().attachDefault(application);
Here is a sample content for the application:
public class MyRestletApplication extends Application {
#Override
public Restlet createInboundRoot() {
Router router = new Router(getContext());
TemplateRoute route = router.attach("/test", MyServerResource.class);
// Default matching mode
int defaultMatching = route.getMatchingMode();
return router;
}
}
And the content of the server resource:
public class MyServerResource extends ServerResource {
#Get
public String test() throws Exception {
return "test";
}
}
Hope it helps you,
Thierry

JAX-RS: How to secure REST endpoints?

I am using JBoss AS and JAX-RS for creating REST endpoints.
Lets say my class looks like
#Path("/users")
public class UserResource {
#GET
public Response getAccount() {
return "hello";
}
}
Now getAccount is not authenticated at the moment
Wanted
- I would like to add authentication so that when code hits getAccount the user is authenticated
- I would like the authentication to be driven by annotations instead of XML configurations, if at all possible
- I would like to do the database comparison to see if the user is valid
Problem
- I have never done that so I have no idea how to implement it
- I have googled around a lot and found Jersey examples
UPDATE
- I would like to send authentication credentials with each request and not creating any session
Please guide me with one simple working example and I would try to extend from there
You need is a Stateless Spring Security configuration in front of your JAX RS end points.
I have addressed exact problem you are trying to solve but I don't have my own code to share..
Here is one project which has done the exact thing you are asking, Some wise man has done it all for you ;)
https://github.com/philipsorst/angular-rest-springsecurity
What is the magic ?
You have one unprotected URL which does the Authentication, and set the user roles as well..
Then you return some kind of Token, put it some where in cache which will be expected on every subsequent call..
Upon new request on other protected resources, you will check if the Token is present in your cache/session store ( you need some mechanism to keep track of valid tokens )
If token is resent and valid, you do the programmatic Log-in in Spring Security which ensures that you can use all the Security features spring provides, ( Annotations, JSTL Tags etc.. ) !
Once passed token validation you will get the logged in user details in your controllers ( aka JAX RS resources ) to deal with security further..
If the token was not valid or not present , it would be trapped by failure end point which would return appropriate response ( 401 )
Refer Following Link To Understand How Stateless Spring Security is configured..,
https://github.com/philipsorst/angular-rest-springsecurity/blob/master/src/main/resources/context.xml
See how a user is validated for the first time and a token is generated..
https://github.com/philipsorst/angular-rest-springsecurity/blob/master/src/main/java/net/dontdrinkandroot/example/angularrestspringsecurity/rest/resources/UserResource.java
Here is the class where programmatic login is performed on every request after token
check..
https://github.com/philipsorst/angular-rest-springsecurity/blob/master/src/main/java/net/dontdrinkandroot/example/angularrestspringsecurity/rest/AuthenticationTokenProcessingFilter.java
I solved this with following code.
note Token mechanism will be updated once I do that
I have solved this by modifying the interceptor I have, the following is code
Annotation
#Inherited
#InterceptorBinding
#Target({ ElementType.TYPE, ElementType.METHOD })
#Retention(RetentionPolicy.RUNTIME)
public #interface SecurityChecked {
}
Resource Class
public class SecureResource {
#GET
#SecurityChecked
public Response getUser() {
return Response.ok("authenticated successfully!").build();
}
}
Interceptor class
#Interceptor
#Provider
#ServerInterceptor
#SecurityChecked
public class SecurityCheckInterceptor implements PreProcessInterceptor, AcceptedByMethod {
private static final Logger LOGGER = LoggerFactory.getLogger(SecurityCheckInterceptor.class);
#Nullable
#Override
public ServerResponse preProcess(final HttpRequest request, final ResourceMethod method) throws Failure, WebApplicationException {
final List<String> authToken = request.getHttpHeaders().getRequestHeader("X-AUTH");
if (authToken == null || !isValidToken(authToken.get(0))) {
final ServerResponse serverResponse = new ServerResponse();
serverResponse.setStatus(Response.Status.UNAUTHORIZED.getStatusCode());
return serverResponse;
}
return null;
}
private static boolean isValidToken(#Nonnull final String authToken) {
LOGGER.info("validating token: " + authToken);
return true;
}
#SuppressWarnings("rawtypes")
#Override
public boolean accept(final Class declaring, final Method method) {
// return declaring.isAnnotationPresent(SecurityChecked.class); // if annotation on class
return method.isAnnotationPresent(SecurityChecked.class);
}
}
and then I run my Integration tests by deploying the resource class in JBoss and issuing following commands on command-line
curl --header 'X-AUTH: 1a629d035831feadOOO4uFReLyEW8aTmrCS' http://localhost:8080/market-1.0-SNAPSHOT/rest/login
curl --header 'InvalidHeader: InvalidHeaderValue' http://localhost:8080/market-1.0-SNAPSHOT/rest/login

How can I override the decisions made during JAX-RS Content Negotiation?

I'm using RESTEasy 2.2.1.GA as my JAX-RS implementation to create a client to connect to a third party service provider. (Education.com's REST API if it matters)
To make sure I haven't missed an important implementation detail here are code samples:
Service Interface
#Path("/")
public interface SchoolSearch {
#GET
#Produces({MediaType.APPLICATION_XML})
Collection<SchoolType> getSchoolsByZipCode(#QueryParam("postalcode") int postalCode);
}
Calling Class
public class SimpleSchoolSearch {
public static final String SITE_URL = "http://api.education.com/service/service.php?f=schoolSearch&key=****&sn=sf&v=4";
SchoolSearch service = ProxyFactory.create(SchoolSearch.class, SITE_URL);
public Collection<SchoolType> getSchools() throws Exception {
Collection<SchoolType> schools = new ArrayList<SchoolType>();
Collection<SchoolType> response = service.getSchoolsByZipCode(35803);
schools.addAll(response);
return schools;
}
}
After setting up tests to make this call, I execute and see the following exception being thrown.
org.jboss.resteasy.plugins.providers.jaxb.JAXBUnmarshalException: Unable to find JAXBContext for media type: text/html;charset="UTF-8"
From reading the RESTEasy/JAX-RS documentation, as I understand it, when the response is returned to the client, prior to the unmarshaling of the data, a determination is made (Content Negotiation??) about which mechanism to use for unmarshalling. (I think we're talking about a MessageBodyReader here but I'm unsure.) From looking at the body of the response, I see that what is returned is properly formatted XML, but the content negotiation (via HTTP header content-type is indeed text/html;charset ="UTF-8") is not allowing the text to be parsed by JAXB.
I think that the implementation is behaving correctly, and it is the service that is in error, however, I don't control the service, but would still like to consume it.
So that being said:
Am I correct in my understanding of why the exception is thrown?
How do I work around it?
Is there a simple one line annotation that can force JAXB to unmarshal the data, or will I need to implement a custom MessageBodyReader? (If that is even the correct class to implement).
Thanks!
Follow Up:
I just wanted to post the few changes I made to Eiden's answer. I created a ClientExecutionInterceptor using his code and the information available at Resteasy ClientExecutionInterceptor documentation. My final class looks like
#Provider
#ClientInterceptor
public class SimpleInterceptor implements ClientExecutionInterceptor {
#Override
public ClientResponse execute(ClientExecutionContext ctx) throws Exception {
final ClientResponse response = ctx.proceed();
response.getHeaders().putSingle(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML);
return response;
}
}
The big difference is the addition of the #Provider and #ClientExecutionInterceptor annotations. This should insure that the interceptor is properly registered.
Also, just for completeness, I registered the Interceptor slightly differently for my tests. I used:
providerFactory.registerProvider(SimpleInterceptor.class);
I'm sure there are several solutions to this problem, but I can only think of one.
Try so set the content-type using a ClientExecutionInterceptor:
public class Interceptor implements ClientExecutionInterceptor {
#Override
public ClientResponse<?> execute(ClientExecutionContext ctx) throws Exception {
final ClientResponse<?> response = ctx.proceed();
response
.getHeaders()
.putSingle(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML);
return response;
}
}
public void getSchools() throws Exception {
ResteasyProviderFactory.getInstance()
.getClientExecutionInterceptorRegistry()
.register( new Interceptor() );
SchoolSearch service =
ProxyFactory.create(SchoolSearch.class, SITE_URL);
}
I dont know about any such annotation, others might do, but a workaround is to create a local proxy. Create a controller, that passes all parameters to education.com using a
java.Net.URL.get()
return the answer that you received, but modify the header. Then connect your client to the local proxy controller.

Categories

Resources