io.werval.modules.jose.JwtPluginTest.java Source code

Java tutorial

Introduction

Here is the source code for io.werval.modules.jose.JwtPluginTest.java

Source

/*
 * 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));
    }
}