> On May 13, 2019, at 11:47 AM, David Blevins <david.blev...@gmail.com> wrote:
> 
> - TOMEE-2517: MP-JWT and BeanValidation adds a fancy new feature that allows 
> users to use Bean Validation to check JWTs.  You simply write a validation 
> constraints for against the JsonWebToken and annotate your method.  A method 
> no longer needs to use @RolesAllowed and can be very expressive and specific 
> through the power of bean validation.
> 
> - TOMEE-2517: MP-JWT and BeanValidation Example.  Any new feature needs 
> documentation or it doesn't exist.  The example is functional and clean.  The 
> README is barely there and will need more work.

Ok, TOMEE-2517 has been rewritten and merged.  All tests passed on my machine, 
we'll see what the CI says.

The heart of this feature is that it lets you replace uses of `@RolesAllowed` 
with annotations of your own creation.  Simply create Bean Validation 
annotations that validate against the MicroProfile JWT `JsonWebToken` interface.

A short example, where you previously had this:

    @Path("/movies")
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    @RequestScoped
    public class MovieService {
    
        private Map<Integer, Movie> store = new ConcurrentHashMap<>();
    
        @GET
        @RolesAllowed({"manager", "user"})
        public List<Movie> getAllMovies() {
            return new ArrayList<>(store.values());
        }
    
        @POST
        @RolesAllowed("manager")
        public void addMovie(Movie newMovie) {
            store.put(newMovie.getId(), newMovie);
        }
    }

You can now do:

    @Path("/movies")
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    @RequestScoped
    public class MovieService {
    
        private Map<Integer, Movie> store = new ConcurrentHashMap<>();
    
        @GET
        @User
        @Manager
        public List<Movie> getAllMovies() {
            return new ArrayList<>(store.values());
        }
    
        @POST
        @Manager
        public void addMovie(Movie newMovie) {
            store.put(newMovie.getId(), newMovie);
        }
    }

Of course, you can now start to validate other parts of the JWT besides 
`groups`, such as `aud` or `iss`:

    @Path("/movies")
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    @RequestScoped
    public class MovieService {
    
        private Map<Integer, Movie> store = new ConcurrentHashMap<>();
    
        @GET
        @Issuer("https://movies.example.com";)
        @User
        @Manager
        public List<Movie> getAllMovies() {
            return new ArrayList<>(store.values());
        }
    
        @POST
        @Audience("movies")
        @Manager
        public void addMovie(Movie newMovie) {
            store.put(newMovie.getId(), newMovie);
        }
    }


To make this work under the covers, we have to basically split a developer's 
bean class into two parts which are used by the different code that needs to 
validate.

So if you have a class called Foo that uses Bean Validation constraints for 
both JWT and Return value, you will get the following two classes generated:

 - `Foo$$ReturnConstraints` used by an updated version of `BValInterceptor`, 
which is a CDI interceptor for return and parameter validation
 - `Foo$$JwtConstraints` used by `ValidationInterceptor`, which is a JAX-RS 
`ContainerRequestFilter` that does the JWT validation

To determine if we need to do any of this for a particular class, we have 
`ClassValidationData`. If it looks as there are JWT constraints on the class, 
we use `ClassValidationGenerator` to handle the generation, which in turn 
delegates to these classes to do the ASM work:

 - `JwtValidationGenerator`
 - `ReturnValidationGenerator`

Effectively we're sorting your Bean Validation annotations into groups based on 
what they validate.

Overall, I consider this feature a pre-standard prototype.  The goal is to:

 - show it to as many people as we can
 - get people to see the appeal
 - get everyone thinking about what missing pieces might have to be created if 
we were to do it cleanly in a spec

There would likely need to be both Bean Validation and MicroProfile JWT 
specification updates for this.

What the Bean Validation spec improvements might be is an open question.  There 
are a few ways we could approach it.


-David


Reply via email to