org.elasticsearch.upgrades.TokenBackwardsCompatibilityIT.java Source code

Java tutorial

Introduction

Here is the source code for org.elasticsearch.upgrades.TokenBackwardsCompatibilityIT.java

Source

/*
 * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
 * or more contributor license agreements. Licensed under the Elastic License;
 * you may not use this file except in compliance with the Elastic License.
 */
package org.elasticsearch.upgrades;

import org.apache.http.HttpHeaders;
import org.apache.http.HttpHost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.message.BasicHeader;
import org.elasticsearch.Version;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.ResponseException;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.test.rest.yaml.ObjectPath;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

public class TokenBackwardsCompatibilityIT extends AbstractUpgradeTestCase {

    public void testGeneratingTokenInOldCluster() throws Exception {
        assumeTrue("this test should only run against the old cluster", clusterType == CLUSTER_TYPE.OLD);
        final StringEntity tokenPostBody = new StringEntity("{\n" + "    \"username\": \"test_user\",\n"
                + "    \"password\": \"x-pack-test-password\",\n" + "    \"grant_type\": \"password\"\n" + "}",
                ContentType.APPLICATION_JSON);
        Response response = client().performRequest("POST", "_xpack/security/oauth2/token", Collections.emptyMap(),
                tokenPostBody);
        assertOK(response);
        Map<String, Object> responseMap = entityAsMap(response);
        String token = (String) responseMap.get("access_token");
        assertNotNull(token);
        assertTokenWorks(token);

        StringEntity oldClusterToken = new StringEntity("{\n" + "    \"token\": \"" + token + "\"\n" + "}",
                ContentType.APPLICATION_JSON);
        Response indexResponse = client().performRequest("PUT",
                "token_backwards_compatibility_it/doc/old_cluster_token1", Collections.emptyMap(), oldClusterToken);
        assertOK(indexResponse);

        response = client().performRequest("POST", "_xpack/security/oauth2/token", Collections.emptyMap(),
                tokenPostBody);
        assertOK(response);
        responseMap = entityAsMap(response);
        token = (String) responseMap.get("access_token");
        assertNotNull(token);
        assertTokenWorks(token);
        oldClusterToken = new StringEntity("{\n" + "    \"token\": \"" + token + "\"\n" + "}",
                ContentType.APPLICATION_JSON);
        indexResponse = client().performRequest("PUT", "token_backwards_compatibility_it/doc/old_cluster_token2",
                Collections.emptyMap(), oldClusterToken);
        assertOK(indexResponse);
    }

    public void testTokenWorksInMixedOrUpgradedCluster() throws Exception {
        assumeTrue("this test should only run against the mixed or upgraded cluster",
                clusterType == CLUSTER_TYPE.MIXED || clusterType == CLUSTER_TYPE.UPGRADED);
        Response getResponse = client().performRequest("GET",
                "token_backwards_compatibility_it/doc/old_cluster_token1");
        assertOK(getResponse);
        Map<String, Object> source = (Map<String, Object>) entityAsMap(getResponse).get("_source");
        assertTokenWorks((String) source.get("token"));
    }

    public void testMixedCluster() throws Exception {
        assumeTrue("this test should only run against the mixed cluster", clusterType == CLUSTER_TYPE.MIXED);
        assumeTrue("the master must be on the latest version before we can write", isMasterOnLatestVersion());
        Response getResponse = client().performRequest("GET",
                "token_backwards_compatibility_it/doc/old_cluster_token2");
        assertOK(getResponse);
        Map<String, Object> source = (Map<String, Object>) entityAsMap(getResponse).get("_source");
        final String token = (String) source.get("token");
        assertTokenWorks(token);

        final StringEntity body = new StringEntity("{\"token\": \"" + token + "\"}", ContentType.APPLICATION_JSON);
        Response invalidationResponse = client().performRequest("DELETE", "_xpack/security/oauth2/token",
                Collections.emptyMap(), body);
        assertOK(invalidationResponse);
        assertTokenDoesNotWork(token);

        // create token and refresh on version that supports it
        final StringEntity tokenPostBody = new StringEntity("{\n" + "    \"username\": \"test_user\",\n"
                + "    \"password\": \"x-pack-test-password\",\n" + "    \"grant_type\": \"password\"\n" + "}",
                ContentType.APPLICATION_JSON);
        try (RestClient client = getRestClientForCurrentVersionNodesOnly()) {
            Response response = client.performRequest("POST", "_xpack/security/oauth2/token",
                    Collections.emptyMap(), tokenPostBody);
            assertOK(response);
            Map<String, Object> responseMap = entityAsMap(response);
            String accessToken = (String) responseMap.get("access_token");
            String refreshToken = (String) responseMap.get("refresh_token");
            assertNotNull(accessToken);
            assertNotNull(refreshToken);
            assertTokenWorks(accessToken);

            final StringEntity tokenRefresh = new StringEntity("{\n" + "    \"refresh_token\": \"" + refreshToken
                    + "\",\n" + "    \"grant_type\": \"refresh_token\"\n" + "}", ContentType.APPLICATION_JSON);
            response = client.performRequest("POST", "_xpack/security/oauth2/token", Collections.emptyMap(),
                    tokenRefresh);
            assertOK(response);
            responseMap = entityAsMap(response);
            String updatedAccessToken = (String) responseMap.get("access_token");
            String updatedRefreshToken = (String) responseMap.get("refresh_token");
            assertNotNull(updatedAccessToken);
            assertNotNull(updatedRefreshToken);
            assertTokenWorks(updatedAccessToken);
            assertTokenWorks(accessToken);
            assertNotEquals(accessToken, updatedAccessToken);
            assertNotEquals(refreshToken, updatedRefreshToken);
        }
    }

    public void testUpgradedCluster() throws Exception {
        assumeTrue("this test should only run against the mixed cluster", clusterType == CLUSTER_TYPE.UPGRADED);
        Response getResponse = client().performRequest("GET",
                "token_backwards_compatibility_it/doc/old_cluster_token2");
        assertOK(getResponse);
        Map<String, Object> source = (Map<String, Object>) entityAsMap(getResponse).get("_source");
        final String token = (String) source.get("token");

        // invalidate again since this may not have been invalidated in the mixed cluster
        final StringEntity body = new StringEntity("{\"token\": \"" + token + "\"}", ContentType.APPLICATION_JSON);
        Response invalidationResponse = client().performRequest("DELETE", "_xpack/security/oauth2/token",
                Collections.singletonMap("error_trace", "true"), body);
        assertOK(invalidationResponse);
        assertTokenDoesNotWork(token);

        getResponse = client().performRequest("GET", "token_backwards_compatibility_it/doc/old_cluster_token1");
        assertOK(getResponse);
        source = (Map<String, Object>) entityAsMap(getResponse).get("_source");
        final String workingToken = (String) source.get("token");
        assertTokenWorks(workingToken);

        final StringEntity tokenPostBody = new StringEntity("{\n" + "    \"username\": \"test_user\",\n"
                + "    \"password\": \"x-pack-test-password\",\n" + "    \"grant_type\": \"password\"\n" + "}",
                ContentType.APPLICATION_JSON);
        Response response = client().performRequest("POST", "_xpack/security/oauth2/token", Collections.emptyMap(),
                tokenPostBody);
        assertOK(response);
        Map<String, Object> responseMap = entityAsMap(response);
        String accessToken = (String) responseMap.get("access_token");
        String refreshToken = (String) responseMap.get("refresh_token");
        assertNotNull(accessToken);
        assertNotNull(refreshToken);
        assertTokenWorks(accessToken);

        final StringEntity tokenRefresh = new StringEntity("{\n" + "    \"refresh_token\": \"" + refreshToken
                + "\",\n" + "    \"grant_type\": \"refresh_token\"\n" + "}", ContentType.APPLICATION_JSON);
        response = client().performRequest("POST", "_xpack/security/oauth2/token", Collections.emptyMap(),
                tokenRefresh);
        assertOK(response);
        responseMap = entityAsMap(response);
        String updatedAccessToken = (String) responseMap.get("access_token");
        String updatedRefreshToken = (String) responseMap.get("refresh_token");
        assertNotNull(updatedAccessToken);
        assertNotNull(updatedRefreshToken);
        assertTokenWorks(updatedAccessToken);
        assertTokenWorks(accessToken);
        assertNotEquals(accessToken, updatedAccessToken);
        assertNotEquals(refreshToken, updatedRefreshToken);
    }

    private void assertTokenWorks(String token) throws IOException {
        Response authenticateResponse = client().performRequest("GET", "_xpack/security/_authenticate",
                Collections.emptyMap(), new BasicHeader(HttpHeaders.AUTHORIZATION, "Bearer " + token));
        assertOK(authenticateResponse);
        assertEquals("test_user", entityAsMap(authenticateResponse).get("username"));
    }

    private void assertTokenDoesNotWork(String token) {
        ResponseException e = expectThrows(ResponseException.class,
                () -> client().performRequest("GET", "_xpack/security/_authenticate", Collections.emptyMap(),
                        new BasicHeader(HttpHeaders.AUTHORIZATION, "Bearer " + token)));
        assertEquals(401, e.getResponse().getStatusLine().getStatusCode());
        Response response = e.getResponse();
        assertEquals(
                "Bearer realm=\"security\", error=\"invalid_token\", error_description=\"The access token expired\"",
                response.getHeader("WWW-Authenticate"));
    }

    private boolean isMasterOnLatestVersion() throws Exception {
        Response response = client().performRequest("GET", "_cluster/state");
        assertOK(response);
        final String masterNodeId = ObjectPath.createFromResponse(response).evaluate("master_node");
        response = client().performRequest("GET", "_nodes");
        assertOK(response);
        ObjectPath objectPath = ObjectPath.createFromResponse(response);
        return Version.CURRENT
                .equals(Version.fromString(objectPath.evaluate("nodes." + masterNodeId + ".version")));
    }

    private RestClient getRestClientForCurrentVersionNodesOnly() throws IOException {
        Response response = client().performRequest("GET", "_nodes");
        assertOK(response);
        ObjectPath objectPath = ObjectPath.createFromResponse(response);
        Map<String, Object> nodesAsMap = objectPath.evaluate("nodes");
        List<HttpHost> hosts = new ArrayList<>();
        for (Map.Entry<String, Object> entry : nodesAsMap.entrySet()) {
            Map<String, Object> nodeDetails = (Map<String, Object>) entry.getValue();
            Version version = Version.fromString((String) nodeDetails.get("version"));
            if (Version.CURRENT.equals(version)) {
                Map<String, Object> httpInfo = (Map<String, Object>) nodeDetails.get("http");
                hosts.add(HttpHost.create((String) httpInfo.get("publish_address")));
            }
        }

        return buildClient(restClientSettings(), hosts.toArray(new HttpHost[0]));
    }
}