I have a resource, which is secured, if I remove the authentication, it appears to work, but then without the security, then what is the point?
Here is my code :
#POST
#Path("/secured")
#Timed
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
#UnitOfWork
#RolesAllowed("mainUser")
public Response aliveSecure(#Auth User user, CustomRequest test)
{
CustomResponse resp = new CustomResponse();
System.out.println(test.getMessage());
return Response.status(Response.Status.ACCEPTED).entity(resp).build();
}
The CustomRequest and CustomResponse types are pretty standard POJOs, they just hold a string called "Message" - they are actually identical, but this is just an exercise I am trying to complete for the sake of learning DropWizard.
Now, if I remove the #Auth stuff here, and the #RolesAllowed, etc - making it a insecure, then the method performs as normal - but as is, this is the error I get when trying to start the application.
org.glassfish.jersey.server.model.ModelValidationException: Validation of the application resource model has failed during application initialization.
! [[FATAL] No injection source found for a parameter of type public CustomRequest at index 0.;
The auth manual reads it clear -
If you want to use #Auth to inject a custom principal type into your
resource.
Hence you shall ensure adding the following to your Service that extends io.dropwizard.Application
#Override
public void run(SomeConfigThatExtendsConfiguration config, Environment environment) throws Exception {
....
environment.jersey().register(new AuthValueFactoryProvider.Binder<>(User.class));
}
Related
I am trying to use #auth and #valid annotation together in one of the APIs in my resource class and it is not being intialized and gives following error at runtime :
Caused by: org.glassfish.jersey.server.model.ModelValidationException:
Validation of the application resource model has failed during
application initialization. [[FATAL] No injection source found for a
parameter of type public javax.ws.rs.core.Response
this is what my api declaration looks like :
#POST
#ApiOperation(value = "Add feature"
#Path("/add/")
#RolesAllowed({"ADMINISTRATORS"})
public Response addTransactionType(#ApiParam(hidden = true) #Auth User user, #Valid CreateRequest request) throws Exception {
whereas the other api method which doesn't have #auth and #valid annotation together works fine, for example :
#GET
#ApiOperation(value = "get detail")
#Path("/detail/{id}")
#RolesAllowed({"USERS"})
public Response get(#ApiParam(hidden = true) #Auth User user, #PathParam("id") int id) throws Exception {
I am using java 17 with dropwizard 2.
I tried changing the order of parameters being passed and removing #valid annotation but nothing worked. Checked if there are multiple apis sharing same path and that doesn't seem to be the case. also have registered the auth binder as well in the app, so that doesn't seem to be the issue as well.
I'm building a simple API framework on top of Weld CDI and Undertow, to get familiar with the CDI Portable Extension programming model. It's a strict subset of JAX-RS:
#Default
#Path("/dogs")
public class Dogs {
#Inject
private MyService service;
#GET
public Response get(#HeaderParam("DogDebug") String debugParam, #Inject DebugService debugger) { return BLAH; }
#Path("/{id}")
#GET
public Response getById(#PathParam("id") String param) { return BLAH; }
}
My CDI Portable Extension collects up all the AnnotatedTypes that have the Path annotation. When CDI finishes booting, an Undertow webserver starts and all the collected types (and their paths) are registered with an Undertow RoutingHandler.
The Extension is responsible for building HttpHandlers for each method that's annotated with #GET/#POST etc...
public HttpHandler getHandler(AnnotatedMethod<?> producer) {
Object contextualHandler = manager.createInstance()
.select(producer.getDeclaringType().getJavaClass()).get();
Preconditions.checkNotNull(contextualHandler, "Could not obtain a contextual reference to a handler for this");
Object result = producer.getJavaMember().invoke(contextualHandler);
Response response;
if(!(result instanceof Response)) {
response = Response.ok(result).build();
} else {
response = (Response) result;
}
response.write(exchange);
}
As you can see, right now the handler is using plain-ol Java Reflection to call the resource method.
I'd like to make method parameter injection work, as shown in my example above. I can use the BeanManager and metadata to grab the right parameters when actually running the handler, but ...
How can I validate the injection point? i.e. with an AnnotatedType I got from a ProcessAnnotatedType event, how can I validate an arbitrary method as if it were a producer or Constructor or event observer?
Update: So far, I've gotten pretty far with the InjectableMethod class from Deltaspike. It inspects the method and creates an InjectionPoint that can be passed to BeanManager.validate. However, it doesn't have much usage in the publicly Googleable code of the world.
If I'm understanding you correctly, then BeanManager#createInjectionPoint(AnnotatedParameter) is what you're looking for in terms of creating and validating the injection points. No need for DeltaSpike or other implementation specifics, it's already part of the spec.
In my organisation, when I want to expose an API, I have to declare it with a swagger contract, same for any update, and it can take multiple weeks before the creation or change is taken into account.
That's why we've come with the idea to declare only one contract for all the APIs we need to expose, and manage the routing in an applicative reverse proxy (the request would include the necessary metadata to allow to route to the appropriate endpoint) :
{
"genericHttpRequest" : base64encodedByteArrayOfAnyHttpRequest
}
Now the question is :
how to manage this request without reimplementing HTTP ? Is it possible to put back the array of byte into a structured HttpServletRequest ?
/**
* Manage a generic request
*/
#RequestMapping(value = "/genericRequest", method = RequestMethod.POST)
public #ResponseBody void manageGenericRequest(#RequestBody GenericHttpRequestDto body) {
byte[] genericHttpRequest = body.getGenericHttpRequest();
//(...)
}
Spring will inject a HttpServletRequest if it is set as a method parameter. Furthermore, wildcard path mappings will enable the methods to be matched to every request:
#RestController
#RequestMapping("/generic-endpoint/**")
public class DemoController {
#RequestMapping
public ResponseEntity<Object> genericGetRequest(HttpServletRequest httpServletRequest) {
return ResponseEntity.ok().body(httpServletRequest.getMethod());
}
}
Optionally, you could return a ResponseEntity to gain more control over your HTTP response.
I am reading through the book RESTful Java with JAX-RS 2.0, 2nd Edition and am struggling to understand how Subresource Locators work, below is a cut-down version of the example provided.
CustomerDatabaseResource class
#Path("/customers")
public class CustomerDatabaseResource {
#Path("{database}-db")
public CustomerResource getDatabase(#PathParam("database") String db) {
// find the instance based on the db parameter
CustomerResource resource = locateCustomerResource(db);
return resource;
}
protected CustomerResource locateCustomerResource(String db) {
...
}
}
CustomerResource Class
public class CustomerResource {
private Map<Integer, Customer> customerDB =
new ConcurrentHashMap<Integer, Customer>();
private AtomicInteger idCounter = new AtomicInteger();
public CustomerResource(Map<Integer, Customer> customerDB)
{
this.customerDB = customerDB;
}
#GET
#Path("{id}")
#Produces("application/xml")
public StreamingOutput getCustomer(#PathParam("id") int id) {
...
}
So I understand that as a request such as GET /customers/northamerica-db/333 comes in, will first match the expression on the method CustomerDatabaseResource.getDatabase() which based upon the location, will create the correct instance of CustomerResource.
What I don't understand is what happens next...
The instance resource gets returned, but returned to where?
How does the web service know to then match and process the remaining part of the request with the method CustomerResource.getCustomer()? I guess this is because The CustomerDataBaseResource class doesn't have a #GET, but I don't really understand how the transition happens.
Is this specific to RESTEasy?
The instance resource gets returned, but returned to where?
It's get's returned to the request processing engine and continues to look for a matching method (inside the return resource object), just like any other request.
How does the web service know to then match and process the remaining part of the request with the method CustomerResource.getCustomer()? I guess this is because The CustomerDataBaseResource class doesn't have a #GET, but I don't really understand how the transition happens
Resource locators aren't supposed to be annotated with Http Methods. That's how they are known to be locators. Since it is not the resource method to be called, it should not be annotated. Imagine this
public class CustomerResource {
#PUT
#Path("{id}")
public Response updateCustomer(Customer customer) {}
#POST
#Path("{id}")
public Response createCustomer(Customer customer) {}
}
If CustomerDataBaseResource.getDatabase() were to be annotated with an Http method, then we couldn't hit the above methods. All the locator needs is the #Path, and the URI matching will continue starting from that path.
/customers/database-us
Once the CustomerResource is created, if the request uri is /customers/database-us/123, then now the next logical step is to find a matching resource method based on the URI, so will be looking for something annotated with #Path that will match 123. Then the Http method is checked.
Is this specific to RESTEasy?
Going through the jax-rs spec, I don't see anything about sub-resource locators, but Jersey also implements this exact behavior. I've read the book you are referring to, and from what I remember, the author doesn't really get much into anything that is implementation specific, but does mention common feautres that most implementers implemented, that is not part of the spec. Maybe this is one of those things.
UPDATE
So it is in the spec. Go to the link and download the spec. You will find everything under 3.4.1 Sub Resources and some algorithm info for request matching in 3.7.2 Request Matching
I have a Jersey REST API and am using a ContainerRequestFilter to handle authorization. I'm also using #ManagedAsync on all endpoints so that my API can serve thousands of concurrent requests.
My authorization filter hits a remote service, but when the filter is run, Jersey hasn't yet added the current thread to it's internal ExecutorService, so I'm completely losing the async benefits.
Can I tell Jersey that I want this ContainerRequestFilter to be asynchronous?
#Priority(Priorities.AUTHORIZATION)
public class AuthorizationFilter implements ContainerRequestFilter
{
#Inject
private AuthorizationService authSvc;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException
{
String authToken = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
// HITS A REMOTE SERVER
AuthorizationResponse authResponse = authSvc.authorize(authToken);
if (!authResponse.isAuthorized())
{
requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED)
.entity("unauthorized!")
.build());
}
}
}
And here's an example resource:
#Path("/stuff")
#Produces(MediaType.APPLICATION_JSON)
public class StuffResource
{
#GET
#Path("/{id}")
#ManagedAsync
public void getById(#PathParam("id") long id, #Suspended final AsyncResponse ar)
{
Stuff s;
// HIT THE DATABASE FOR STUFF
ar.resume(s);
}
}
UPDATE Just heard back from the Jersey guys, and this is not possible as of 2.7. Only the resource method itself is invoked asynchronously, not filters. Any suggestions for proceeding still welcome.
This is not built in to Jersey as of 2.7.
#ManagedAsync is useless if you have any filters or interceptors that do any serious work (like hit a remote authorization service). They may add the ability to run filters asynchronously in the future, but for now you're on your own.
UPDATE - there are other ways...
After a long and perilous journey, I have found a very hacky solution that I'm using in the short term. Here is a rundown of what I tried and why it failed/worked.
Guice AOP - failed
I use Guice for DI (getting Guice injection to work with Jersey is a feat in itself!), so I figured I could use Guice AOP to get around the issue. Though Guice injection works, it is impossible to get Guice to create resource classes with Jersey 2, so Guice AOP cannot work with resource class methods. If you are trying desperately to get Guice to create resource classes with Jersey 2, don't waste your time because it will not work. This is a well-known problem.
HK2 AOP - RECOMMENDED SOLUTION
HK2 just recently released an AOP feature, see this question for details on how to get it working.
Monitoring - also worked
This is not for the faint of heart, and it is completely discouraged in the Jersey docs. You can register and ApplicationEventListener and override onRequest to return a RequestEventListener that listens for RESOURCE_METHOD_START and calls an authentication/authorization service. This event is triggered from the #ManagedAsync thread, which is the whole goal here. One caveat, the abortWith method is a no-op, so this won't work quite like a normal ContainerRequestFilter. Instead, you can throw an exception if auth fails instead, and register an ExceptionMapper to handle your exception. If someone is bold enough to give this a try, let me know and I'll post code.
I am not sure if this is what you were looking for but, have you looked into Spring's OncePerRequestFilter? I am currently using it for my authorization layer where each request goes through some filter that extends this OncePerRequestFilter depending on how my filters are mapped to the URLs. Here's a quick overview of how I am using it:
Authentication/Authorization of a resource in Dropwizard
I am not very clear on the async dispatch parts of these filters but I hope this link atleast sheds some light to what you are trying to achieve!
We use Spring security for authentication/authorization. I worked around the problem using a sub-resource locator with empty path as shown below:
#Path("/customers")
public class CustomerResource {
#Inject
private CustomerService customerService;
#Path("")
public CustomerSubResource delegate() {
final Authentication auth = SecurityContextHolder.getContext().getAuthentication();
return new CustomerSubResource(auth);
}
public class CustomerSubResource {
private final Authentication auth;
public CustomerSubResource(final Authentication auth) {
this.auth = auth;
}
#POST
#Path("")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
#ManagedAsync
public void createCustomer(final Customer customer, #Suspended final AsyncResponse response) {
// Stash the Spring security context into the Jersey-managed thread
SecurityContextHolder.getContext().setAuthentication(this.auth);
// Invoke service method requiring pre-authorization
final Customer newCustomer = customerService.createCustomer(customer);
// Resume the response
response.resume(newCustomer);
}
}
}