I hadn't really seen this covered anywhere, so I was curious how people are 
handling this?

I'm using camel's REST dsl within the java dsl, running in Karaf.

Java EE and Spring both have concepts of annotation based authorization to 
specific endpoints, and it seemed like it would be nice to incorporate this 
into the REST dsl.

Having found nothing off the shelf, I attempted to solve this myself by 
implementing my own RoutePolicy.  In the onExchangeBegin method I look for the 
Authorization header on the camel Mesage, if it exists, I jump through some 
hoops with LoginContext to validate the user/password and also the Role.  

If something is wrong, I set an exception on the exchange

In practice it works like this:

in the configure() block I setup a couple route policies, one for admins, one 
for viewers (these map to JAAS roles, karaf is the realm)

RoleAuthPolicy admin = new RoleAuthPolicy("karaf", "admin");
RoleAuthPolicy viewer = new RoleAuthPolicy("karaf", "viewer");


And then in use, you append the routePolicy in the builder:

rest("servers")
                
.get().produces("application/json").route().routeId("get-servers").routePolicy(viewer).to("bean:serverManager?method=getServers()").endRest()
                
.get("/{id}").produces("application/json").route().routeId("get-server").routePolicy(viewer).to("bean:serverManager?method=getServer(${headers.id})").endRest()
                
.post().route().routeId("post-server").routePolicy(admin).to("bean:serverManager?method=postServer(${body})").endRest();


Pros of this method:

you can have very granular authorization
it fits nicely with the builder patterns

Cons:

I created custom exceptions to handle authentication, and authorization errors 
and those have to be defined on the route so the user is prompted with what to 
do:

        onException(UnauthenticatedException.class)
                .handled(true)
                .log(LoggingLevel.WARN, log, "${exception.message}")
                .process(exchange -> {
                    exchange.getIn().setHeader("WWW-Authenticate", "Basic");
                    exchange.getIn().setBody("Unauthenticated");
                    exchange.getIn().setHeader(Exchange.CONTENT_TYPE, 
"text/plain");
                    exchange.getIn().setHeader(Exchange.HTTP_RESPONSE_CODE, 
HttpServletResponse.SC_UNAUTHORIZED);
                });

        onException(UnauthorizedException.class)
                .handled(true)
                .log(LoggingLevel.WARN, log, "${exception.message}")
                .process(exchange -> {
                    exchange.getIn().setBody("Unauthorized");
                    exchange.getIn().setHeader(Exchange.CONTENT_TYPE, 
"text/plain");
                    exchange.getIn().setHeader(Exchange.HTTP_RESPONSE_CODE, 
HttpServletResponse.SC_FORBIDDEN);
                });

If someone forgets to do this, you get no authentication or authorization

I also didn't love working inside the onExchangeBegin method, because 
exceptions thrown in there are swallowed with a warning message.  So any kind 
of implementation mistake or unexpected situation that throws an exception in 
the onExchangeBegin method leads too open access!!! to defend against this I 
wrap the entire thing in a try catch Exception and then set this exception on 
the exchange.


So i'm curious, how are other people solving this problem?  

Thanks,
Ed

Reply via email to