Java tutorial
/* * Copyright (c) 2015 the original author or authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package io.werval.modules.jose; import com.fasterxml.jackson.databind.JsonNode; import io.werval.api.outcomes.Outcome; import io.werval.modules.jose.filters.RequireRoles; import io.werval.modules.jose.filters.RequireSubject; import io.werval.runtime.routes.RoutesParserProvider; import io.werval.test.WervalHttpRule; import io.werval.util.Maps; import java.time.Instant; import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import org.junit.ClassRule; import org.junit.Test; import static com.jayway.restassured.RestAssured.given; import static com.jayway.restassured.RestAssured.when; import static io.werval.api.context.CurrentContext.application; import static io.werval.api.context.CurrentContext.outcomes; import static io.werval.api.context.CurrentContext.plugin; import static io.werval.api.context.CurrentContext.request; import static io.werval.api.http.Status.OK_CODE; import static io.werval.api.http.Status.UNAUTHORIZED_CODE; import static io.werval.api.mime.MimeTypesNames.APPLICATION_JSON; import static io.werval.modules.json.JSON.json; import static java.util.Collections.singletonMap; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.notNullValue; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; /** * JwtPlugin Test. */ public class JwtPluginTest { @ClassRule public static final WervalHttpRule WERVAL = new WervalHttpRule(new RoutesParserProvider( "POST /login io.werval.modules.jose.JwtPluginTest$Controller.login\n" + "POST /renew JWT.renew\n" + "GET /authenticated io.werval.modules.jose.JwtPluginTest$Controller.authenticated\n" + "GET /authorized io.werval.modules.jose.JwtPluginTest$Controller.authorized\n")); public static class Controller { public Outcome login() { JsonNode body = json().fromJSON(request().body().asBytes()); String email = body.get("email").asText(); String password = body.get("password").asText(); if ("admin@example.com".equals(email) && "admin-password".equals(password)) { HashMap<String, Object> claims = Maps.newHashMap(String.class, Object.class) .put(JWT.CLAIM_SUBJECT, email).put("roles", Arrays.asList("admin")).toMap(); String token = plugin(JWT.class).tokenForClaims(claims); return outcomes().ok().withHeader(application().config().string(JWT.HTTP_HEADER_CONFIG_KEY), token) .build(); } return outcomes().unauthorized().build(); } @RequireSubject public Outcome authenticated() { return outcomes().ok().build(); } @RequireRoles("admin") public Outcome authorized() { return outcomes().ok().build(); } } @Test public void api() { JWT jwt = WERVAL.application().plugin(JWT.class); String token = jwt.tokenForClaims(singletonMap(JWT.CLAIM_SUBJECT, "someone@example.com")); Map<String, Object> parsed = jwt.claimsOfToken(token); assertThat(parsed.get(JWT.CLAIM_SUBJECT), equalTo("someone@example.com")); } @Test public void http() throws InterruptedException { String tokenHeaderName = WERVAL.application().config().string(JWT.HTTP_HEADER_CONFIG_KEY); JWT jwt = WERVAL.application().plugin(JWT.class); // Unauthorized access to authenticated resource when().get("/authenticated").then().statusCode(UNAUTHORIZED_CODE); // Login String token = given().body("{\"email\":\"admin@example.com\",\"password\":\"admin-password\"}") .contentType(APPLICATION_JSON).when().post("/login").then().statusCode(OK_CODE) .header(tokenHeaderName, notNullValue()).log().all().extract().header(tokenHeaderName); // Authenticated access given().header(tokenHeaderName, token).when().get("/authenticated").then().statusCode(OK_CODE); // Authorized access given().header(tokenHeaderName, token).when().get("/authorized").then().statusCode(OK_CODE); // Gather time related claims from token ZoneId utc = ZoneId.of("UTC"); Map<String, Object> claims = jwt.claimsOfToken(token); ZonedDateTime iat = ZonedDateTime.ofInstant(Instant.ofEpochSecond((Long) claims.get(JWT.CLAIM_ISSUED_AT)), utc); ZonedDateTime nbf = ZonedDateTime.ofInstant(Instant.ofEpochSecond((Long) claims.get(JWT.CLAIM_NOT_BEFORE)), utc); ZonedDateTime exp = ZonedDateTime.ofInstant(Instant.ofEpochSecond((Long) claims.get(JWT.CLAIM_EXPIRATION)), utc); // Wait at least one second before renewal so new dates will be different Thread.sleep(1200); // Renew token String renewed = given().header(tokenHeaderName, token).when().post("/renew").then().statusCode(OK_CODE) .header(tokenHeaderName, notNullValue()).log().all().extract().header(tokenHeaderName); // Gather time related claims from renewed token claims = jwt.claimsOfToken(renewed); ZonedDateTime renewedIat = ZonedDateTime .ofInstant(Instant.ofEpochSecond((Long) claims.get(JWT.CLAIM_ISSUED_AT)), utc); ZonedDateTime renewedNbf = ZonedDateTime .ofInstant(Instant.ofEpochSecond((Long) claims.get(JWT.CLAIM_NOT_BEFORE)), utc); ZonedDateTime renewedExp = ZonedDateTime .ofInstant(Instant.ofEpochSecond((Long) claims.get(JWT.CLAIM_EXPIRATION)), utc); // Assert renewed token time related claims are greater than the ones in the original token assertTrue(renewedIat.isAfter(iat)); assertTrue(renewedNbf.isAfter(nbf)); assertTrue(renewedExp.isAfter(exp)); } }