You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by jl...@apache.org on 2018/12/12 16:02:32 UTC
[08/11] tomee git commit: TOMEE-2304 Added 401 and 403 test scenarios
TOMEE-2304 Added 401 and 403 test scenarios
Project: http://git-wip-us.apache.org/repos/asf/tomee/repo
Commit: http://git-wip-us.apache.org/repos/asf/tomee/commit/59c288bb
Tree: http://git-wip-us.apache.org/repos/asf/tomee/tree/59c288bb
Diff: http://git-wip-us.apache.org/repos/asf/tomee/diff/59c288bb
Branch: refs/heads/master
Commit: 59c288bb3e5c7b08afe707b970a7d2cad25eed13
Parents: 2e46043
Author: CesarHernandezGt <cf...@gmail.com>
Authored: Tue Dec 11 23:05:16 2018 -0600
Committer: CesarHernandezGt <cf...@gmail.com>
Committed: Tue Dec 11 23:05:16 2018 -0600
----------------------------------------------------------------------
examples/mp-rest-jwt/README.md | 267 ++++++++++++++++++-
.../org/superbiz/moviefun/rest/MoviesRest.java | 98 ++-----
.../java/org/superbiz/moviefun/MoviesTest.java | 55 ++--
3 files changed, 319 insertions(+), 101 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/tomee/blob/59c288bb/examples/mp-rest-jwt/README.md
----------------------------------------------------------------------
diff --git a/examples/mp-rest-jwt/README.md b/examples/mp-rest-jwt/README.md
index b2dc71f..b4c913f 100644
--- a/examples/mp-rest-jwt/README.md
+++ b/examples/mp-rest-jwt/README.md
@@ -1,4 +1,263 @@
-index-group=Unrevised
-type=page
-status=published
-~~~~~~
+# MP REST JWT
+This is a basic example on how to use MicroProfile JWT in TomEE.
+
+## Run the tests for different scenarios related with JWT validation
+
+ mvn clean test
+
+## Configuration in TomEE
+
+The class `MoviesMPJWTConfigurationProvider.java` provide to TomEE figuration for JWT validation.
+
+ package org.superbiz.moviefun.rest;
+
+ import org.apache.tomee.microprofile.jwt.config.JWTAuthContextInfo;
+
+ import javax.enterprise.context.Dependent;
+ import javax.enterprise.inject.Produces;
+ import java.security.KeyFactory;
+ import java.security.NoSuchAlgorithmException;
+ import java.security.interfaces.RSAPublicKey;
+ import java.security.spec.InvalidKeySpecException;
+ import java.security.spec.X509EncodedKeySpec;
+ import java.util.Base64;
+ import java.util.Optional;
+
+ @Dependent
+ public class MoviesMPJWTConfigurationProvider {
+
+ @Produces
+ Optional<JWTAuthContextInfo> getOptionalContextInfo() throws NoSuchAlgorithmException, InvalidKeySpecException {
+ JWTAuthContextInfo contextInfo = new JWTAuthContextInfo();
+
+ // todo use MP Config to load the configuration
+ contextInfo.setIssuedBy("https://server.example.com");
+
+ final String pemEncoded = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlivFI8qB4D0y2jy0CfEq" +
+ "Fyy46R0o7S8TKpsx5xbHKoU1VWg6QkQm+ntyIv1p4kE1sPEQO73+HY8+Bzs75XwR" +
+ "TYL1BmR1w8J5hmjVWjc6R2BTBGAYRPFRhor3kpM6ni2SPmNNhurEAHw7TaqszP5e" +
+ "UF/F9+KEBWkwVta+PZ37bwqSE4sCb1soZFrVz/UT/LF4tYpuVYt3YbqToZ3pZOZ9" +
+ "AX2o1GCG3xwOjkc4x0W7ezbQZdC9iftPxVHR8irOijJRRjcPDtA6vPKpzLl6CyYn" +
+ "sIYPd99ltwxTHjr3npfv/3Lw50bAkbT4HeLFxTx4flEoZLKO/g0bAoV2uqBhkA9x" +
+ "nQIDAQAB";
+ byte[] encodedBytes = Base64.getDecoder().decode(pemEncoded);
+
+ final X509EncodedKeySpec spec = new X509EncodedKeySpec(encodedBytes);
+ final KeyFactory kf = KeyFactory.getInstance("RSA");
+ final RSAPublicKey pk = (RSAPublicKey) kf.generatePublic(spec);
+
+ contextInfo.setSignerKey(pk);
+
+ return Optional.of(contextInfo);
+ }
+
+ @Produces
+ JWTAuthContextInfo getContextInfo() throws InvalidKeySpecException, NoSuchAlgorithmException {
+ return getOptionalContextInfo().get();
+ }
+ }
+
+## Use MicroProfile JWT in TomEE
+
+The JAX-RS resource `MoviesRest.java` contains several endpoint that are secured using the standard
+annotation `@RolesAllowed`. MicroProfile JWT takes care of performing the validation for incoming
+requests with `Authorization` header providing a signed `Access Token`
+
+
+ package org.superbiz.moviefun.rest;
+
+ import org.superbiz.moviefun.Movie;
+ import org.superbiz.moviefun.MoviesBean;
+
+ import javax.annotation.security.RolesAllowed;
+ import javax.inject.Inject;
+ import javax.ws.rs.*;
+ import javax.ws.rs.core.MediaType;
+ import java.util.List;
+
+ @Path("cinema")
+ @Produces(MediaType.APPLICATION_JSON)
+ @Consumes(MediaType.APPLICATION_JSON)
+ public class MoviesRest {
+
+ @Inject
+ private MoviesBean moviesBean;
+
+ @GET
+ @Produces(MediaType.TEXT_PLAIN)
+ public String status() {
+ return "ok";
+ }
+
+ @GET
+ @Path("/movies")
+ @RolesAllowed({"crud", "read-only"})
+ public List<Movie> getListOfMovies() {
+ return moviesBean.getMovies();
+ }
+
+ @GET
+ @Path("/movies/{id}")
+ @RolesAllowed({"crud", "read-only"})
+ public Movie getMovie(@PathParam("id") int id) {
+ return moviesBean.getMovie(id);
+ }
+
+ @POST
+ @Path("/movies")
+ @RolesAllowed("crud")
+ public void addMovie(Movie newMovie) {
+ moviesBean.addMovie(newMovie);
+ }
+
+ @DELETE
+ @Path("/movies/{id}")
+ @RolesAllowed("crud")
+ public void deleteMovie(@PathParam("id") int id) {
+ moviesBean.deleteMovie(id);
+ }
+
+ @PUT
+ @Path("/movies")
+ @RolesAllowed("crud")
+ public void updateMovie(Movie updatedMovie) {
+ moviesBean.updateMovie(updatedMovie);
+ }
+
+ }
+
+ @Inject
+ @ConfigProperty(name = "java.runtime.version")
+ private String javaVersion;
+
+## About the Test architecture
+
+The test cases from this project are builded using Arquillian. The arquillian configuration can be found in
+`src/test/resources/arquillian.xml`
+
+The class `TokenUtils.java` is used during the test to act as an Authorization server who generates `Access Tokens` based
+on the configuration files `privateKey.pem`,`publicKey.pem`,`Token1.json`, and `Token2.json`.
+
+`nimbus-jose-jwt` is the library used for JWT generation during the testsĀ”.
+
+## Test Scenarios
+
+`MovieTest.java` contains 4 OAuth2 scenarios for different JWT combinations.
+
+ package org.superbiz.moviefun;
+
+ import org.apache.cxf.feature.LoggingFeature;
+ import org.apache.cxf.jaxrs.client.WebClient;
+ import org.apache.johnzon.jaxrs.JohnzonProvider;
+ import org.jboss.arquillian.container.test.api.Deployment;
+ import org.jboss.arquillian.junit.Arquillian;
+ import org.jboss.arquillian.test.api.ArquillianResource;
+ import org.jboss.shrinkwrap.api.ShrinkWrap;
+ import org.jboss.shrinkwrap.api.asset.StringAsset;
+ import org.jboss.shrinkwrap.api.spec.WebArchive;
+ import org.junit.Test;
+ import org.junit.runner.RunWith;
+ import org.superbiz.moviefun.rest.ApplicationConfig;
+ import org.superbiz.moviefun.rest.MoviesMPJWTConfigurationProvider;
+ import org.superbiz.moviefun.rest.MoviesRest;
+
+ import javax.ws.rs.core.Response;
+ import java.net.URL;
+ import java.util.Collection;
+ import java.util.HashMap;
+ import java.util.logging.Logger;
+
+ import static java.util.Collections.singletonList;
+ import static org.junit.Assert.assertTrue;
+
+ @RunWith(Arquillian.class)
+ public class MoviesTest {
+
+ @Deployment(testable = false)
+ public static WebArchive createDeployment() {
+ final WebArchive webArchive = ShrinkWrap.create(WebArchive.class, "test.war")
+ .addClasses(Movie.class, MoviesBean.class, MoviesTest.class)
+ .addClasses(MoviesRest.class, ApplicationConfig.class)
+ .addClass(MoviesMPJWTConfigurationProvider.class)
+ .addAsWebInfResource(new StringAsset("<beans/>"), "beans.xml");
+
+ System.out.println(webArchive.toString(true));
+
+ return webArchive;
+ }
+
+ @ArquillianResource
+ private URL base;
+
+
+ private final static Logger LOGGER = Logger.getLogger(MoviesTest.class.getName());
+
+ @Test
+ public void movieRestTest() throws Exception {
+
+ final WebClient webClient = WebClient
+ .create(base.toExternalForm(), singletonList(new JohnzonProvider<>()),
+ singletonList(new LoggingFeature()), null);
+
+
+ //Testing rest endpoint deployment (GET without security header)
+ String responsePayload = webClient.reset().path("/rest/cinema/").get(String.class);
+ LOGGER.info("responsePayload = " + responsePayload);
+ assertTrue(responsePayload.equalsIgnoreCase("ok"));
+
+
+ //POST (Using token1.json with group of claims: [CRUD])
+ Movie newMovie = new Movie(1, "David Dobkin", "Wedding Crashers");
+ Response response = webClient.reset()
+ .path("/rest/cinema/movies")
+ .header("Content-Type", "application/json")
+ .header("Authorization", "Bearer " + token(1))
+ .post(newMovie);
+ LOGGER.info("responseCode = " + response.getStatus());
+ assertTrue(response.getStatus() == 204);
+
+
+ //GET movies (Using token1.json with group of claims: [read-only])
+ //This test should be updated to use token2.json once TOMEE- gets resolved.
+ Collection<? extends Movie> movies = webClient
+ .reset()
+ .path("/rest/cinema/movies")
+ .header("Content-Type", "application/json")
+ .header("Authorization", "Bearer " + token(1))
+ .getCollection(Movie.class);
+ LOGGER.info(movies.toString());
+ assertTrue(movies.size() == 1);
+
+
+ //Should return a 403 since POST require group of claims: [crud] but Token 2 has only [read-only].
+ Movie secondNewMovie = new Movie(2, "Todd Phillips", "Starsky & Hutch");
+ Response responseWithError = webClient.reset()
+ .path("/rest/cinema/movies")
+ .header("Content-Type", "application/json")
+ .header("Authorization", "Bearer " + token(2))
+ .post(secondNewMovie);
+ LOGGER.info("responseCode = " + responseWithError.getStatus());
+ assertTrue(responseWithError.getStatus() == 403);
+
+
+ //Should return a 401 since the header Authorization is not part of the POST request.
+ Response responseWith401Error = webClient.reset()
+ .path("/rest/cinema/movies")
+ .header("Content-Type", "application/json")
+ .post(new Movie());
+ LOGGER.info("responseCode = " + responseWith401Error.getStatus());
+ assertTrue(responseWith401Error.getStatus() == 401);
+
+ }
+
+
+ private String token(int token_type) throws Exception {
+ HashMap<String, Long> timeClaims = new HashMap<>();
+ if (token_type == 1) {
+ return TokenUtils.generateTokenString("/Token1.json", null, timeClaims);
+ } else {
+ return TokenUtils.generateTokenString("/Token2.json", null, timeClaims);
+ }
+ }
+
+ }
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/tomee/blob/59c288bb/examples/mp-rest-jwt/src/main/java/org/superbiz/moviefun/rest/MoviesRest.java
----------------------------------------------------------------------
diff --git a/examples/mp-rest-jwt/src/main/java/org/superbiz/moviefun/rest/MoviesRest.java b/examples/mp-rest-jwt/src/main/java/org/superbiz/moviefun/rest/MoviesRest.java
index 1ded167..9e67334 100644
--- a/examples/mp-rest-jwt/src/main/java/org/superbiz/moviefun/rest/MoviesRest.java
+++ b/examples/mp-rest-jwt/src/main/java/org/superbiz/moviefun/rest/MoviesRest.java
@@ -39,105 +39,39 @@ public class MoviesRest {
return "ok";
}
+ @GET
+ @Path("/movies")
+ @RolesAllowed({"crud", "read-only"})
+ public List<Movie> getListOfMovies() {
+ return moviesBean.getMovies();
+ }
+
+ @GET
+ @Path("/movies/{id}")
+ @RolesAllowed({"crud", "read-only"})
+ public Movie getMovie(@PathParam("id") int id) {
+ return moviesBean.getMovie(id);
+ }
+
@POST
@Path("/movies")
@RolesAllowed("crud")
- @Produces(MediaType.APPLICATION_JSON)
- @Consumes(MediaType.APPLICATION_JSON)
public void addMovie(Movie newMovie) {
moviesBean.addMovie(newMovie);
}
@DELETE
@Path("/movies/{id}")
- @RolesAllowed("read-only")
+ @RolesAllowed("crud")
public void deleteMovie(@PathParam("id") int id) {
moviesBean.deleteMovie(id);
}
@PUT
@Path("/movies")
+ @RolesAllowed("crud")
public void updateMovie(Movie updatedMovie) {
moviesBean.updateMovie(updatedMovie);
}
- @GET
- @Path("/movies/{id}")
- @RolesAllowed({"read-only","crud"})
- public Movie getMovie(@PathParam("id") int id) {
- return moviesBean.getMovie(id);
- }
-
- @GET
- @Path("/movies")
- @RolesAllowed({"crud", "read-only"})
- public List<Movie> getListOfMovies() {
- return moviesBean.getMovies();
- }
-
-
-// @Inject
-// @Claim("raw_token")
-// private ClaimValue<String> rawToken;
-//
-// @Inject
-// @Claim("iss")
-// private ClaimValue<String> issuer;
-//
-// @Inject
-// @Claim("jti")
-// private ClaimValue<String> jti;
-//
-// @Inject
-// private JsonWebToken jwtPrincipal;
-//
-// @Context
-// private SecurityContext securityContext;
-//
-// @GET
-// @Path("{id}")
-// public Movie find(@PathParam("id") Long id) {
-// return service.find(id);
-// }
-//
-// @GET
-// public List<Movie> getMovies(@QueryParam("first") Integer first, @QueryParam("max") Integer max,
-// @QueryParam("field") String field, @QueryParam("searchTerm") String searchTerm) {
-// return service.getMovies(first, max, field, searchTerm);
-// }
-//
-// @POST
-// @Consumes("application/json")
-// @RolesAllowed("create")
-// public Movie addMovie(Movie movie) {
-// service.addMovie(movie);
-// return movie;
-// }
-//
-// @PUT
-// @Path("{id}")
-// @Consumes("application/json")
-// @RolesAllowed("update")
-// public Movie editMovie(
-// @PathParam("id") final long id,
-// Movie movie
-// ) {
-// service.editMovie(movie);
-// return movie;
-// }
-//
-// @DELETE
-// @Path("{id}")
-// @RolesAllowed("delete")
-// public void deleteMovie(@PathParam("id") long id) {
-// service.deleteMovie(id);
-// }
-//
-// @GET
-// @Path("count")
-// @Produces(MediaType.TEXT_PLAIN)
-// public int count(@QueryParam("field") String field, @QueryParam("searchTerm") String searchTerm) {
-// return service.count(field, searchTerm);
-// }
-
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/tomee/blob/59c288bb/examples/mp-rest-jwt/src/test/java/org/superbiz/moviefun/MoviesTest.java
----------------------------------------------------------------------
diff --git a/examples/mp-rest-jwt/src/test/java/org/superbiz/moviefun/MoviesTest.java b/examples/mp-rest-jwt/src/test/java/org/superbiz/moviefun/MoviesTest.java
index ea622b8..7ba6808 100644
--- a/examples/mp-rest-jwt/src/test/java/org/superbiz/moviefun/MoviesTest.java
+++ b/examples/mp-rest-jwt/src/test/java/org/superbiz/moviefun/MoviesTest.java
@@ -46,10 +46,10 @@ public class MoviesTest {
@Deployment(testable = false)
public static WebArchive createDeployment() {
final WebArchive webArchive = ShrinkWrap.create(WebArchive.class, "test.war")
- .addClasses(Movie.class, MoviesBean.class, MoviesTest.class)
- .addClasses(MoviesRest.class, ApplicationConfig.class)
- .addClass(MoviesMPJWTConfigurationProvider.class)
- .addAsWebInfResource(new StringAsset("<beans/>"), "beans.xml");
+ .addClasses(Movie.class, MoviesBean.class, MoviesTest.class)
+ .addClasses(MoviesRest.class, ApplicationConfig.class)
+ .addClass(MoviesMPJWTConfigurationProvider.class)
+ .addAsWebInfResource(new StringAsset("<beans/>"), "beans.xml");
System.out.println(webArchive.toString(true));
@@ -66,8 +66,8 @@ public class MoviesTest {
public void movieRestTest() throws Exception {
final WebClient webClient = WebClient
- .create(base.toExternalForm(), singletonList(new JohnzonProvider<>()), singletonList(new LoggingFeature()), null);
-
+ .create(base.toExternalForm(), singletonList(new JohnzonProvider<>()),
+ singletonList(new LoggingFeature()), null);
//Testing rest endpoint deployment (GET without security header)
@@ -75,32 +75,57 @@ public class MoviesTest {
LOGGER.info("responsePayload = " + responsePayload);
assertTrue(responsePayload.equalsIgnoreCase("ok"));
+
//POST (Using token1.json with group of claims: [CRUD])
- Movie newMovie = new Movie(1,"David Dobkin","Wedding Crashers");
- Response response = webClient.reset().path("/rest/cinema/movies").header("Content-Type","application/json").header("Authorization", "Bearer " + token(1)).post(newMovie);
+ Movie newMovie = new Movie(1, "David Dobkin", "Wedding Crashers");
+ Response response = webClient.reset()
+ .path("/rest/cinema/movies")
+ .header("Content-Type", "application/json")
+ .header("Authorization", "Bearer " + token(1))
+ .post(newMovie);
LOGGER.info("responseCode = " + response.getStatus());
assertTrue(response.getStatus() == 204);
-
- //GET movies (Using token2.json with group of claims: [read-only])
- final Collection<? extends Movie> movies = webClient
+ //GET movies (Using token1.json with group of claims: [read-only])
+ //This test should be updated to use token2.json once TOMEE- gets resolved.
+ Collection<? extends Movie> movies = webClient
.reset()
.path("/rest/cinema/movies")
- .header("Content-Type","application/json")
+ .header("Content-Type", "application/json")
.header("Authorization", "Bearer " + token(1))
.getCollection(Movie.class);
LOGGER.info(movies.toString());
assertTrue(movies.size() == 1);
- }
+ //Should return a 403 since POST require group of claims: [crud] but Token 2 has only [read-only].
+ Movie secondNewMovie = new Movie(2, "Todd Phillips", "Starsky & Hutch");
+ Response responseWithError = webClient.reset()
+ .path("/rest/cinema/movies")
+ .header("Content-Type", "application/json")
+ .header("Authorization", "Bearer " + token(2))
+ .post(secondNewMovie);
+ LOGGER.info("responseCode = " + responseWithError.getStatus());
+ assertTrue(responseWithError.getStatus() == 403);
+
+
+ //Should return a 401 since the header Authorization is not part of the POST request.
+ Response responseWith401Error = webClient.reset()
+ .path("/rest/cinema/movies")
+ .header("Content-Type", "application/json")
+ .post(new Movie());
+ LOGGER.info("responseCode = " + responseWith401Error.getStatus());
+ assertTrue(responseWith401Error.getStatus() == 401);
+
+ }
+
private String token(int token_type) throws Exception {
HashMap<String, Long> timeClaims = new HashMap<>();
- if(token_type==1){
+ if (token_type == 1) {
return TokenUtils.generateTokenString("/Token1.json", null, timeClaims);
- }else{
+ } else {
return TokenUtils.generateTokenString("/Token2.json", null, timeClaims);
}
}