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

com.networknt.rule.epam.EpamTokenRequestTransformAction Maven / Gradle / Ivy

Go to download

A yaml rule action implementation to get the epam authorization token and inject into the Authorization header for any external epam API request.

The newest version!
package com.networknt.rule.epam;

import com.networknt.client.ClientConfig;
import com.networknt.client.oauth.TokenResponse;
import com.networknt.config.Config;
import com.networknt.config.JsonMapper;
import com.networknt.config.PathPrefixAuth;
import com.networknt.http.client.ssl.TLSConfig;
import com.networknt.rule.RequestTransformAction;
import com.networknt.rule.RuleActionValue;
import com.networknt.rule.RuleConstants;
import com.networknt.status.Status;
import com.networknt.utility.ModuleRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import java.net.*;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.security.KeyStore;
import java.time.Duration;
import java.util.*;

/**
 * It is called from the request transform interceptor from the light-gateway to get the e-PAM API access token with
 * the passed in grant_type, client_id and scope. The plugin will create a brand new SSLContext with keystore to
 * enable authentication with PING OAuth server.
 *
 * @author Steve Hu
 */
public class EpamTokenRequestTransformAction implements RequestTransformAction {
    public static final String GET_TOKEN_ERROR = "ERR10052";
    private static final Logger logger = LoggerFactory.getLogger(EpamTokenRequestTransformAction.class);
    // change the config to static so that it can cache the token retrieved until expiration time.
    private static final EpamConfig config = EpamConfig.load();
    private static HttpClient client;

    public EpamTokenRequestTransformAction() {
        if(logger.isInfoEnabled()) logger.info("EpamTokenRequestTransformAction is constructed");
        List masks = new ArrayList<>();
        masks.add("certPassword");
        ModuleRegistry.registerPlugin(
                EpamTokenRequestTransformAction.class.getPackage().getImplementationTitle(),
                EpamTokenRequestTransformAction.class.getPackage().getImplementationVersion(),
                EpamConfig.CONFIG_NAME,
                EpamTokenRequestTransformAction.class.getName(),
                Config.getNoneDecryptedInstance().getJsonMapConfigNoCache(EpamConfig.CONFIG_NAME),
                masks);
    }

    @Override
    public void performAction(String ruleId, String actionId, Map objMap, Map resultMap, Collection actionValues) {
        String requestPath = (String)objMap.get("requestPath");
        if(logger.isTraceEnabled()) logger.trace("ruleId = {} actionId = {} requestPath = {}", ruleId, actionId, requestPath);


        for(PathPrefixAuth pathPrefixAuth: config.getPathPrefixAuths()) {
            if(requestPath.startsWith(pathPrefixAuth.getPathPrefix())) {
                if(logger.isTraceEnabled())
                    logger.trace("found with requestPath = {} prefix = {}", requestPath, pathPrefixAuth.getPathPrefix());
                if(System.currentTimeMillis() >= pathPrefixAuth.getExpiration()) {
                    if(logger.isTraceEnabled()) logger.trace("Cached token {} is expired with current time {} and expired time {}", pathPrefixAuth.getAccessToken() != null ? pathPrefixAuth.getAccessToken().substring(0, 20) : null, System.currentTimeMillis(), pathPrefixAuth.getExpiration());
                    String requestBody = (String)objMap.get("requestBody");
                    if(logger.isTraceEnabled()) logger.trace("requestBody = {}", requestBody);
                    TokenResponse tokenResponse = getAccessToken(pathPrefixAuth.getTokenUrl(), requestBody);
                    if(tokenResponse != null) {
                        pathPrefixAuth.setExpiration(System.currentTimeMillis() + tokenResponse.getExpiresIn() * 1000 - 60000);
                        pathPrefixAuth.setAccessToken(tokenResponse.getAccessToken());
                        if(logger.isTraceEnabled()) logger.trace("Got a new token {} and cached it with expiration time {}", pathPrefixAuth.getAccessToken() != null ? pathPrefixAuth.getAccessToken().substring(0, 20) : null, pathPrefixAuth.getExpiration());
                    } else {
                        if(logger.isTraceEnabled()) logger.trace("Failed to get a new token");
                        // need to return an error message to the caller.
                        resultMap.put("contentType", "application/json");
                        Status status = new Status(GET_TOKEN_ERROR);
                        resultMap.put("statusCode", status.getStatusCode());
                        resultMap.put("responseBody", status.toString());
                        return;
                    }
                }
                // either a new token is retrieved or cached token is not expired. Put the token into the Authorization header.
                if(pathPrefixAuth.getAccessToken() != null) {
                    resultMap.put("contentType", "application/json");
                    resultMap.put("statusCode", 200);
                    // construct the response body
                    Map responseBody = new HashMap<>();
                    responseBody.put("access_token", pathPrefixAuth.getAccessToken());
                    responseBody.put("token_type", "Bearer");
                    responseBody.put("expires_in", (pathPrefixAuth.getExpiration() - System.currentTimeMillis()) / 1000);
                    resultMap.put("responseBody", JsonMapper.toJson(responseBody));
                    return;
                }
            }
        }
    }

    private TokenResponse getAccessToken(String serverUrl, String requestBody) {
        String certFileName = config.getCertFilename(); // PKCS12 format
        if(logger.isTraceEnabled()) logger.trace("certFileName = {} serverUrl = {}", certFileName, serverUrl);
        String certPassword = config.getCertPassword();
        TokenResponse tokenResponse = null;
        if(client == null) {
            try {
                //  create keystore
                KeyStore keyStore = KeyStore.getInstance("PKCS12");
                keyStore.load(Config.getInstance().getInputStreamFromFile(certFileName), certPassword.toCharArray());
                KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
                kmf.init(keyStore, certPassword.toCharArray());
                SSLContext sslContext = SSLContext.getInstance("TLS");
                sslContext.init(kmf.getKeyManagers(), null, null);

                HttpClient.Builder clientBuilder = HttpClient.newBuilder()
                        .followRedirects(HttpClient.Redirect.NORMAL)
                        .connectTimeout(Duration.ofMillis(ClientConfig.get().getTimeout()))
                        .sslContext(sslContext);
                if(config.getProxyHost() != null) {
                    if(logger.isTraceEnabled())
                        logger.trace("use proxy {}:{}", config.getProxyHost(), config.getProxyPort());
                    clientBuilder.proxy(ProxySelector.of(new InetSocketAddress(config.getProxyHost(), config.getProxyPort() == 0 ? 443 : config.getProxyPort())));
                }
                if(config.isEnableHttp2()) {
                    if(logger.isTraceEnabled()) logger.trace("enable http2 is true");
                    clientBuilder.version(HttpClient.Version.HTTP_2);
                } else {
                    if(logger.isTraceEnabled()) logger.trace("enable http2 is false");
                    clientBuilder.version(HttpClient.Version.HTTP_1_1);
                }
                // this a workaround to bypass the hostname verification in jdk11 http client.
                Map tlsMap = (Map)ClientConfig.get().getMappedConfig().get(ClientConfig.TLS);
                if(tlsMap != null && !Boolean.TRUE.equals(tlsMap.get(TLSConfig.VERIFY_HOSTNAME))) {
                    if(logger.isTraceEnabled()) logger.trace("disable hostname verification");
                    final Properties props = System.getProperties();
                    props.setProperty("jdk.internal.httpclient.disableHostnameVerification", Boolean.TRUE.toString());
                }
                client = clientBuilder.build();

            } catch (Exception e) {
                logger.error("Cannot create HttpClient:", e);
                return null;
            }
        }
        try {
            if(serverUrl == null) {
                logger.error("tokenUrl is null");
                return null;
            }

            HttpRequest request = HttpRequest.newBuilder()
                    .uri(URI.create(serverUrl))
                    .headers("Content-Type", "application/x-www-form-urlencoded", "accept", "application/json")
                    .POST(HttpRequest.BodyPublishers.ofString(requestBody))
                    .build();

            HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
            if(response.statusCode() == 200) {
                // construct a token response and return it.
                if(logger.isTraceEnabled()) logger.trace("response body {}", response.body().toString());
                Map map = JsonMapper.string2Map(response.body().toString());
                if(map != null) {
                    tokenResponse = new TokenResponse();
                    tokenResponse.setAccessToken((String)map.get("access_token"));
                    tokenResponse.setTokenType((String)map.get("token_type"));
                    tokenResponse.setExpiresIn((Integer)map.get("expires_in"));
                    if(logger.isTraceEnabled()) logger.trace("tokenResponse = {}", tokenResponse.toString());
                    return tokenResponse;
                } else {
                    logger.error("response body cannot be parsed as a JSON {}", response.body());
                    return null;
                }
            } else {
                logger.error("Error in getting the token with status code {} and body {}", response.statusCode(), response.body().toString());
                return null;
            }
        } catch (Exception e) {
            logger.error("Exception:", e);
            return null;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy