In Quarkus (resteasy reactive), is there a way to get hold of the "ResourceInfo" in an HTTP Authentication Mechanism?
What I'm trying to do is read an annotation that is defined on the resource class or method, in order to choose an authentication mechanism based on it.
Injecting the ResourceInfo directly in the mechanism class does not work (and also, it is application scoped and not request scoped, so not sure it could work). I also couldn't find the info I need in the RoutingContext parameter.
I have also tried adding a ContainerRequestFilter, in which injecting the ResourceInfo with #Context works well, but I think perhaps the filters are called after the httpAuthenticationMechanism.authenticate(), because it's not called in my test when the endpoint requires authentication.
Is there another way to do this?
----> To clarify with code what I would like to do:
have different JAX-RS resources with a custom #Authorization annotations with different "api names" like this:
#Path("/jwttest")
#ApplicationScoped
#Authorization("jwttest")
public class JWTTestController {
...
}
#Path("/oidctest")
#ApplicationScoped
#Authorization("myoidc")
public class OIDCTestController {
...
}
and then different configs like this:
myframework.auth.jwttest.type=jwt
myframework.auth.jwttest.issuer=123
myframework.auth.jwttest.audience=456
myframework.auth.myoidc.type=oidc
myframework.auth.myoidc.auth-server-url=myurl
And in the HttpAuthenticationMechanism, find the value of #Authorization, and based on it, call another provider like suggested in https://quarkus.io/guides/security-customization#dealing-with-more-than-one-httpauthenticationmechanism with the right api name so that it can load the config.
Related
In a quarkus app, I'd like to develop my own SecurityIdentity, because existing ones do not fit my need (I need to check identity by getting roles from multiple micro-services). Maybe a custom SecurityIdentity is not the best option, but looking at documentation it seems to be what I need (another option is to define a ContainerRequestFilter implementation but this is at lower level and less integrated).
Is there any way to do it? I tested the following:
#Provider
#PreMatching
class CustomSecurityIdentity: SecurityIdentity {
override fun getPrincipal(): Principal {
....
Then from an endpoint I simply defined:
#GET
#Produces(MediaType.APPLICATION_JSON)
#RolesAllowed("administrator")
#Path("/")
fun checkAuth(): Response {
...
I put some breakpoints everywhere but it never comes through my Identity Provider where the #RolesAllowed should (the way I see it) force it.
Is it possible to make it work?
Say I have a web service / a REST resource that is called with some HTTP header parameters. The resource method builds a complex data object (currently a POJO) and eventually returns it to the client (via Gson as JSON, but that doesn't matter).
So I have this call hierarchy:
#Path(foo) ProjectResource #GET getProject()
-> new Project()
-> new List<Participant> which contains lots of new Participant()s
-> new Affiliation()
If I want the Affiliation object to be e.g. populated in English or German depending on a header parameter, I have to pass that as a parameter down the chain. I want to avoid having to do that. Maybe this is just fundamentally impossible, but it feels so wrong. All these objects only live inside the request, so wouldn't it be convenient to be able to access information tied to the request from anywhere?
I was hoping I could e.g. define a CDI #RequestScoped object that initialized itself (or gets populated by some WebFilter) and that I can then inject where I might need it.
But obviously that doesn't work from inside the POJOs, and I also had trouble getting hold of the headers from inside the request-scoped object.
I've read many SO questions/answers about EJBs and JAX-RS Context and CDI but I can't wrap my head around it.
Am I expecting too much? Is passing down the parameter really the preferred option?
If I understand what you need, you can try the following (just wrote this solution from the top of my head, but it should work):
Defining a class to store the data you need
Define a class annotated with #RequestScoped which will store the data you need:
#RequestScoped
public class RequestMetadata {
private Locale language;
// Default constructor, getters and setters ommited
}
Ensure you are using the #RequestScoped annotation from the javax.enterprise.context package.
Creating a request filter
Create a ContainerRequestFilter to populate the RequestMetadata:
#Provider
#PreMatching
public class RequestMetadataFilter implements ContainerRequestFilter {
#Inject
private RequestMetadata requestMetadata;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
requestMetadata.setLanguage(requestContext.getLanguage());
}
}
Performing the injection
And then you can finally perform the injection of the RequestMetadata using #Inject:
#Stateless
public class Foo {
#Inject
private RequestMetadata requestMetadata;
...
}
Please, be aware that anywhere is too broad: The injection will work into beans managed by the container, such as servlets, JAX-RS classes, EJB and CDI beans, for example.
You won't be able to perform injections into beans created by yourself neither into JPA entities.
I have a JAX-RS 2.0 DynamicFeature that registers a response filter on certain classes.
As part of that registration, I'd like the DynamicFeature implementation to set a property that can then be retrieved by the filter instance.
It looks like I should be able to do this. In my DynamicFeature implementation, I should be able to call:
featureContext.property("foo", "bar");
...and it's my understanding that that property is supposed to be durable.
Then, in my filter, I should be able to do:
#Context
private Configuration myConfiguration;
...and should be able to read that property in my filter method:
if (this.myConfiguration != null) {
final Object propertyValue = this.myConfiguration.getProperty("foo");
if ("bar".equals(propertyValue)) {
// Hooray
}
}
But this doesn't work. The Configuration instance that is injected into my filter is non-null, which is good, but contains one irrelevant property in it that I didn't set.
I know that I could accomplish something similar by using an injected ServletContext as the go-between, but I was hoping to avoid using the Servlet APIs here, since this seemed like a "clean" JAX-RS mechanism.
Am I misunderstanding how the Configuration object reachable from within a DynamicFeature's configure method is to be used?
There are an option in ServiceStack to add routes dynamically, using IAppHost.Routes.Add. This is quite handy as it allows reusable services to be packed in a form of plugin and attached to actual applications.
To illustrate, here's an excerpt of application host configuration that enables a plugin and attaches routes:
class AppHost: AppHostHttpListenerBase {
public override void Configure(Container container) {
// other stuff
Plugins.Add(new MyReusablePlugin());
Routes.Add(typeof(string), "/stuff", "GET");
Routes.Add(typeof(string), "/other-stuff", "POST");
}
}
The problem is, I can't find a way to specify that authentication is required for those dynamically added routes. As of ServiceStack 4.0.15 there are no overload to Routes.Add that allow specifying that those routes require authentication. Is it possible (maybe in newer versions)?
Ideally, such a mechanism should support specifying roles/permissions, just like RequiredRoleAttribue or RequiresAnyRoleAttribute.
One more thing: I'm aware of Global request filters and it looks like they are a bit too global to be a valid solution here, but I might be wrong. If it's possible to achieve the same result (including ability to specify required roles/permissions) using request filters I would be glad to accept an example of such apporach as an answer.
You can do this using the AddAttributes extension method provided on Type:
typeof(YourType).AddAttributes(new RequiredRoleAttribute("Admin"));
So you would need to do this in addition to the Routes.Add method.
suppose i have a jax-rs resource class that looks like this:
#Path("/nodes")
public class NodeResource {
//Temp - those injections should work
#EJB
ListNodesLocal nodeList;
//stuff
}
and i want some sort of lifecycle callback so i can manually lookup that field via JNDI because injection isnt working for me yet (using jboss 6 m5. see this issue : https://jira.jboss.org/browse/JBAS-8575).
ideally im looking for something like
#PostConstruct
private void init() {
//manual JNDI to come here
}
can i do this somehow ? i've tried javax.annotation.PostConstruct to no avail. is there something that works?
Since you linked to jboss in your question this answer assumes you're using the Resteasy implementation of JAX-RS. You can register interceptors to hook into the lifecycle. See here. That's how I was able to use Shiro annotations to authorize clients who want to invoke my API.