Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Optionally Protect a Resource with Custom Dropwizard Filter

I'm using Dropwizard 0.9.2 and I want to create a resource that requires no authentication for GET and requires basic authentication for POST.

I have tried

@Path("/protectedPing")
@Produces(MediaType.TEXT_PLAIN)
public class ProtectedPing {

@GET
public String everybody() {

    return "pingpong";
}

@PermitAll
@POST
public String authenticated(){
    return "secret pingpong";
}

with

CachingAuthenticator<BasicCredentials, User> ca = new CachingAuthenticator<>(environment.metrics(), ldapAuthenticator, cbSpec);
AdminAuthorizer authorizer = new AdminAuthorizer();
BasicCredentialAuthFilter<User> bcaf = new BasicCredentialAuthFilter.Builder<User>().setAuthenticator(ca).setRealm("test-oauth").setAuthorizer(authorizer).buildAuthFilter();
environment.jersey().register(bcaf);
environment.jersey().register(RolesAllowedDynamicFeature.class);
environment.jersey().register(new AuthValueFactoryProvider.Binder<>(User.class));
environment.jersey().register(new ProtectedPing());

This seems to result in all requests to "/protectedPing" requiring basic auth.

In Dropwizard 0.9.2 the documentation says to create a custom filter if I have a resource that is optionally protected. I'm assuming I need to do that, but I don't know where to start, or if that I what I actually need to do.

like image 467
monknomo Avatar asked Mar 01 '16 20:03

monknomo


1 Answers

this is more of a jersey problem than a dropwizard problem. You can have a look here: https://jersey.java.net/documentation/latest/filters-and-interceptors.html

Essentially what you want is:

  1. Create an annotation that indicates that you want to test for authentication (e.g. @AuthenticatePost)

  2. Create the resource and annotate the correct method with @AuthenticatePost

  3. Create your authentication filter (probably kind of like what you did above).

  4. In the dynamic feature, test for the annotation to be present on the passed in resource. This will hold true for post, false for get. Then register the AuthenticationFilter directly on the resource method instead of globally on the resource.

This would be a semi-complete example of how I would solve this:

public class MyDynamicFeature implements DynamicFeature {

    @Override
    public void configure(ResourceInfo resourceInfo, FeatureContext context) {
        if(resourceInfo.getResourceMethod().getAnnotation(AuthenticateMe.class) != null ) {
            context.register(MyAuthFilter.class);
        }
    }

    public class MyAuthFilter implements ContainerRequestFilter {

        @Override
        public void filter(ContainerRequestContext requestContext) throws IOException {
            // do authentication here
        }

    }

    public @interface AuthenticateMe {

    }

    @Path("myPath")
    public class MyResource {

        @GET
        public String get() {
            return "get-method";
        }

        @POST
        @AuthenticateMe
        public String post() {
            return "post-method";
        }
    }
}

Note, the DynamicFeature checks that the Authenticate Annotation is present, before registering the authentication with the feature context.

I hope that helps,

let me know if you have any questions.

like image 105
pandaadb Avatar answered Nov 15 '22 19:11

pandaadb