All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sap.cloud.sdk.cloudplatform.security.RefreshJwtTokenCommand Maven / Gradle / Ivy

/*
 * Copyright (c) 2019 SAP SE or an SAP affiliate company. All rights reserved.
 */

package com.sap.cloud.sdk.cloudplatform.security;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import org.apache.http.HttpHeaders;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;

import com.auth0.jwt.JWT;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.json.JsonSanitizer;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandKey;
import com.netflix.hystrix.HystrixCommandProperties;
import com.netflix.hystrix.HystrixThreadPoolKey;
import com.netflix.hystrix.HystrixThreadPoolProperties;
import com.sap.cloud.sdk.cloudplatform.CloudPlatform;
import com.sap.cloud.sdk.cloudplatform.CloudPlatformAccessor;
import com.sap.cloud.sdk.cloudplatform.ScpCfCloudPlatform;
import com.sap.cloud.sdk.cloudplatform.connectivity.HttpClientAccessor;
import com.sap.cloud.sdk.cloudplatform.connectivity.HttpEntityUtil;
import com.sap.cloud.sdk.cloudplatform.exception.ShouldNotHappenException;
import com.sap.cloud.sdk.cloudplatform.security.exception.TokenRequestFailedException;
import com.sap.cloud.sdk.frameworks.hystrix.Command;
import com.sap.cloud.sdk.frameworks.hystrix.HystrixUtil;

import lombok.Data;

/**
 * This command is used by the CloudFoundry security setup to refresh an expired JWT, given a refresh token. NOTE: This
 * is accepted duplicate code to connectivity-scp-cf:TokenRequest.
 */
class RefreshJwtTokenCommand extends Command
{
    @Data
    private static class CommandSetterBuilder
    {
        private final Class> commandClass;

        HystrixCommand.Setter build()
        {
            final String groupKey = HystrixUtil.getGlobalKey(commandClass);
            final String commandKey = "refreshTokenRetrievalCommand";

            final HystrixCommandProperties.Setter commandProperties =
                HystrixCommandProperties
                    .Setter()
                    .withExecutionTimeoutInMilliseconds(6000)
                    .withCircuitBreakerEnabled(true)
                    .withCircuitBreakerSleepWindowInMilliseconds(6000)
                    .withFallbackEnabled(false);

            return HystrixCommand.Setter
                .withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey))
                .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey(groupKey))
                .andCommandKey(HystrixCommandKey.Factory.asKey(commandKey))
                .andThreadPoolPropertiesDefaults(
                    HystrixThreadPoolProperties
                        .Setter()
                        .withCoreSize(10)
                        .withQueueSizeRejectionThreshold(100)
                        .withMaxQueueSize(100))
                .andCommandPropertiesDefaults(commandProperties);
        }
    }

    private final String encodedJwt;
    private final String refreshToken;

    RefreshJwtTokenCommand( @Nonnull final String encodedJwt, @Nonnull final String refreshToken )
    {
        super(new CommandSetterBuilder(RefreshTokenRetrievalCommand.class).build());

        this.encodedJwt = encodedJwt;
        this.refreshToken = refreshToken;
    }

    @Override
    protected String run()
    {
        // Get the XSUAA credentials from the environment variables
        final CloudPlatform cloudPlatform = CloudPlatformAccessor.getCloudPlatform();

        if( !(cloudPlatform instanceof ScpCfCloudPlatform) ) {
            throw new ShouldNotHappenException(
                "The current Cloud platform is not an instance of "
                    + ScpCfCloudPlatform.class.getSimpleName()
                    + ". Please make sure to specify a dependency to com.sap.cloud.s4hana.cloudplatform:core-scp-cf.");
        }

        final JsonObject xsuaaServiceCredentials =
            ((ScpCfCloudPlatform) cloudPlatform).getXsuaaServiceCredentials(JWT.decode(encodedJwt));

        final String clientid = xsuaaServiceCredentials.get("clientid").getAsString();
        final String xsuaaUrl = xsuaaServiceCredentials.get("url").getAsString();
        final String clientSecret = xsuaaServiceCredentials.get("clientsecret").getAsString();

        // Assemble the request
        final URI uri;
        try {
            uri = new URI((xsuaaUrl.endsWith("/") ? xsuaaUrl : xsuaaUrl + "/") + "oauth/token");
        }
        catch( final URISyntaxException e ) {
            throw new TokenRequestFailedException(e);
        }

        final HttpPost tokenRequest = new HttpPost(uri);

        tokenRequest.setHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.toString());
        tokenRequest.setHeader("Content-Type", ContentType.APPLICATION_FORM_URLENCODED.toString());

        try {
            tokenRequest.setEntity(
                new StringEntity(
                    "client_id="
                        + clientid
                        + "&client_secret="
                        + clientSecret
                        + "&grant_type=refresh_token&token_format=jwt&refresh_token="
                        + refreshToken));
        }
        catch( final UnsupportedEncodingException e ) {
            throw new TokenRequestFailedException(e);
        }

        // Execute request
        final HttpResponse response;
        try {
            response = HttpClientAccessor.getHttpClient().execute(tokenRequest);
        }
        catch( final IOException e ) {
            throw new TokenRequestFailedException(e);
        }

        final int statusCode = response.getStatusLine().getStatusCode();
        if( statusCode >= 400 && statusCode <= 599 ) {
            throw new TokenRequestFailedException(
                "Refresh JWT request failed with status code "
                    + statusCode
                    + ": "
                    + response.getStatusLine().getReasonPhrase());
        }

        final String responseBody;
        try {
            responseBody = HttpEntityUtil.getResponseBody(response);
        }
        catch( final IOException e ) {
            throw new TokenRequestFailedException(e);
        }

        final JsonObject responseBodyJson =
            new JsonParser().parse(JsonSanitizer.sanitize(responseBody)).getAsJsonObject();

        @Nullable
        final String encodedJwt = responseBodyJson.get("access_token").getAsString();

        return encodedJwt;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy