bbilger/jrestless

How to get GatewayIdentity?

Closed this issue · 8 comments

With 0.4.0 I have more of a question than an actual issue using my own authentication filter. Maybe just a little confused about the new factory setup. Here's what I had before for 0.3.0 to retrieve a gatewayIdentity in a filter:

public class AuthFilter implements ContainerRequestFilter {

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        GatewayIdentity gatewayIdentity = (GatewayIdentity) requestContext
               .getProperty(GatewayIdentityContextFactory.PROPERTY_NAME);

               
        ...
    }
}

How might I do this with 0.4.0? I am using IAM authentication for the API Gateway -- not custom or cognito userpool.

Hi,
Short answer:

public class AuthFilter implements ContainerRequestFilter {
    /*
     * this is a proxy and request scoped so you get the values
     * for the current request when you access it in #filter
     */
    private final GatewayRequest gatewayRequest;
    @Inject
    public AuthFilter(GatewayRequest gatewayRequest) {
        this.gatewayRequest = gatewayRequest;
    }
    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        GatewayIdentity gatewayIdentity = gatewayRequest.getRequestContext().getIdentity();
        ...
    }
}

Long answer:
I changed the way how GatewayRequest gets injected and use a ReferencingFactory, instead of putting it into the ContainerRequestContext. So GatewayRequest is no longer available through ContainerRequestContext but you can inject it. Sorry for this breaking change but I wanted to get it right since that's the way all other Jersey containers (grizzly, etc.) do it.

Also GatewayIdentity and GatewayRequestContext are not injectable directly anymore. So one has to inject GatewayRequest and access GatewayRequestContext like thisgatewayRequest.getRequestContext() and GatewayIdentity like this gatewayRequest.getRequestContext().getIdentity(). The problem here was that GatewayIdentity and GatewayRequestContext could be null (at least in testing environments) and if they are null the request will fail since a proxiable injectee may not be null. Again sorry for this breaking change but this was a bug and it was a mistake to make GatewayRequestContext and GatewayIdentity injectable in previous versions. Well this wasn't an issue in previous versions since those objects weren't proxiable but now that they are this is an issue.

There are two filters doing the very same (NOTE: those filters get registered automatically when registering the GatewayFeature):

(maybe I'll add an IAM auth filter in one of the next releases)

I also update the incompatible changes section in the release notes for v0.4.0.

Thanks @bbilger. Also, after dancing around a org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl it is worth mentioning too that GatewayFeature.class needs to be registered in the resource re:

ResourceConfig().register(GatewayFeature.class)...

... saw you did that in your examples.

@dkobia yes it is absolutely necessary to register the GatewayFeature. Hope things work for you, now

Solves one problem but creates another. With local testing outside of AWS I get the following error. I understand why -- the injected GatewayRequest is null, just not sure how to work around it.

WARN  Could not send response error 500: javax.servlet.ServletException: A MultiException has 1 exceptions.  They are:
1. java.lang.IllegalStateException: Proxiable context org.glassfish.jersey.process.internal.RequestScope@440adf07 findOrCreate returned a null for descriptor SystemDescriptor(
        implementation=com.jrestless.aws.gateway.GatewayFeature$ReferencingGatewayRequestFactory
        contracts={com.jrestless.aws.gateway.io.GatewayRequest}
        scope=org.glassfish.jersey.process.internal.RequestScoped
        qualifiers={}
        descriptorType=PROVIDE_METHOD
        descriptorVisibility=NORMAL
        metadata=
        rank=0
        loader=org.glassfish.hk2.utilities.binding.AbstractBinder$2@28f2d056
        proxiable=true
        proxyForSameScope=false
        analysisName=null
        id=148
        locatorId=0
        identityHashCode=765164962
        reified=true) and handle ServiceHandle(SystemDescriptor(
        implementation=com.jrestless.aws.gateway.security.CustomAuthorizerFilter
        contracts={javax.ws.rs.container.ContainerRequestFilter}
        scope=javax.inject.Singleton
        qualifiers={org.glassfish.jersey.internal.inject.Custom}
        descriptorType=CLASS
        descriptorVisibility=NORMAL
        metadata=
        rank=2000
        loader=null
        proxiable=null
        proxyForSameScope=null
        analysisName=null
        id=178
        locatorId=0
        identityHashCode=400766947
        reified=true),92483839)

@dkobia
May I assume you are using JerseyTest?

another user had a similar issue/question #17

Short answer:
use a different resource config for your tests, one where you do not register GatewayFeature (that's the main reason I dropped GatewayResourceConfig so one does not need to inherit from it).

Long answer:
in the test environment the request does not go through GatewayRequestHandler (ServiceRequestHandler or SnsRequestHandler)
It is a different environment and GatewayFeature is specific to the actual AWS Lambda environment or rather the handler (GatewayRequestHandler).

Note in case you do not want to use JerseyTest but want do a "native" test with GatewayRequestHandler, you could follow what I do in on of my integration tests "https://github.com/bbilger/jrestless/blob/master/aws/gateway/jrestless-aws-gateway-handler/src/test/java/com/jrestless/aws/gateway/handler/GatewayRequestObjectHandlerIntTest.java"
I do, however, not recommend this, since in general:
Your resources (the JAX-RS classes) should not be environment dependent. This means that you should not inject GatewayRequest into an endpoint (the same applies to HttpServletRequest in a Servlet environment). If you need some data from this environment specific data, then you should fetch the data in some filter and make it available to your resources through those filters.

Note: The next task (P1) for this framework is documentation and I'll try to make those things more clear.

Hope this helps! Let me know if you have any other issues, questions or doubts.

Also I think I'll pull up the Gateway/Service/SnsFeature registration into the handlers itself and don't leave it to the user anymore. That should make things more clear and less fragile.

@bbilger separate ResourceConfigs was definitely the way to go anyway. Thanks again -- I think all my problems are resolved.

Thanks for your feedback!
Closing the issue, then.