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

com.networknt.oauth.key.handler.Oauth2KeyKeyIdGetHandler Maven / Gradle / Ivy

/*
 * Copyright (c) 2016 Network New Technologies Inc.
 *
 * 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 com.networknt.oauth.key.handler;

import com.networknt.body.BodyHandler;
import com.networknt.client.oauth.KeyRequest;
import com.networknt.client.oauth.OauthHelper;
import com.networknt.config.Config;
import com.networknt.config.JsonMapper;
import com.networknt.handler.LightHttpHandler;
import com.networknt.monad.Result;
import com.networknt.oauth.cache.AuditInfoHandler;
import com.networknt.oauth.cache.ClientUtil;
import com.networknt.oauth.cache.OAuthConfig;
import com.networknt.oauth.cache.model.AuditInfo;
import com.networknt.oauth.cache.model.OauthService;
import com.networknt.security.SecurityConfig;
import com.networknt.status.Status;
import com.networknt.exception.ApiException;
import com.networknt.exception.ClientException;
import com.networknt.utility.HashUtil;

import io.undertow.server.HttpServerExchange;
import io.undertow.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.Locale;
import java.util.Map;

import static io.undertow.util.Headers.AUTHORIZATION;
import static io.undertow.util.Headers.BASIC;

/**
 * Get certificate that is used to verify the particular kid passed as a parameter.
 *
 * @author Steve Hu
 */
public class Oauth2KeyKeyIdGetHandler  extends AuditInfoHandler implements LightHttpHandler {
    static final Logger logger = LoggerFactory.getLogger(Oauth2KeyKeyIdGetHandler.class);
    static final String CONFIG_SECURITY = "security";
    static final String KEY_NOT_FOUND = "ERR12017";
    static final String MISSING_AUTHORIZATION_HEADER = "ERR12002";
    static final String CLIENT_NOT_FOUND = "ERR12014";
    static final String RUNTIME_EXCEPTION = "ERR10010";
    static final String UNAUTHORIZED_CLIENT = "ERR12007";
    static final String INVALID_KEY_ID = "ERR12030";
    private static final String BASIC_PREFIX = BASIC + " ";
    private static final String LOWERCASE_BASIC_PREFIX = BASIC_PREFIX.toLowerCase(Locale.ENGLISH);
    private static final int PREFIX_LENGTH = BASIC_PREFIX.length();
    private static final String COLON = ":";
    private final static OAuthConfig config = (OAuthConfig) Config.getInstance().getJsonObjectConfig(OAuthConfig.CONFIG_NAME, OAuthConfig.class);

    @SuppressWarnings("unchecked")
    @Override
    public void handleRequest(HttpServerExchange exchange) throws Exception {

        String keyId = exchange.getQueryParameters().get("keyId").getFirst();
        if(logger.isDebugEnabled()) logger.debug("keyId = " + keyId);
        // find the location of the certificate
        SecurityConfig config = SecurityConfig.load(CONFIG_SECURITY);
        Map certificateConfig = config.getCertificate();
        String provider_id = config.getProviderId();
        if (getProviderId(keyId)==null || "00".equals(getProviderId(keyId)) || provider_id.equals(getProviderId(keyId))) {
            if (getProviderId(keyId)==null  || provider_id.equals(getProviderId(keyId))) {
                // Standalone light-oauth server
                // check if client_id and client_secret in header are valid pair.
                HeaderValues values = exchange.getRequestHeaders().get(AUTHORIZATION);
                String authHeader;
                if(values != null) {
                    authHeader = values.getFirst();
                } else {
                    setExchangeStatus(exchange, MISSING_AUTHORIZATION_HEADER);
                    processAudit(exchange);
                    return;
                }
                if(authHeader == null) {
                    setExchangeStatus(exchange, MISSING_AUTHORIZATION_HEADER);
                    processAudit(exchange);
                    return;
                }
                if (authenticate(authHeader) == null) {
                    setExchangeStatus(exchange, UNAUTHORIZED_CLIENT);
                    processAudit(exchange);
                    return;
                }
            }
            String filename = (String)certificateConfig.get(getKeyId(keyId));
            if(filename != null) {
                String content = Config.getInstance().getStringFromFile(filename);
                if(logger.isDebugEnabled()) logger.debug("certificate = " + content);
                if(content != null) {
                    exchange.getResponseHeaders().add(Headers.CONTENT_TYPE, "application/text");
                    exchange.getResponseSender().send(content);
                } else {
                    setExchangeStatus(exchange, KEY_NOT_FOUND, keyId);
                }
            } else {
                setExchangeStatus(exchange, INVALID_KEY_ID, keyId);
            }
        } else {
            //Federation light-oauth server
            String content = getCertificateFromProvider(getProviderId(keyId), getKeyId(keyId));
            if(logger.isDebugEnabled()) logger.debug("certificate from provider = " + content);
            if(content != null) {
                exchange.getResponseHeaders().add(Headers.CONTENT_TYPE, "application/text");
                exchange.getResponseSender().send(content);
            } else {
                setExchangeStatus(exchange, KEY_NOT_FOUND, keyId);
            }
        }

        processAudit(exchange);
    }

    @SuppressWarnings("unchecked")
    private String authenticate(String authHeader) throws ApiException {
        String result = null;
        if (authHeader.toLowerCase(Locale.ENGLISH).startsWith(LOWERCASE_BASIC_PREFIX)) {
            String base64Challenge = authHeader.substring(PREFIX_LENGTH);
            String plainChallenge;
            try {
                ByteBuffer decode = FlexBase64.decode(base64Challenge);
                // assume charset is UTF_8
                Charset charset = StandardCharsets.UTF_8;
                plainChallenge = new String(decode.array(), decode.arrayOffset(), decode.limit(), charset);
                logger.debug("Found basic auth header %s (decoded using charset %s) in %s", plainChallenge, charset, authHeader);
                int colonPos;
                if ((colonPos = plainChallenge.indexOf(COLON)) > -1) {
                    String clientId = plainChallenge.substring(0, colonPos);
                    String clientSecret = plainChallenge.substring(colonPos + 1);
                    // match with db/cached user credentials.
                    Result resultClient = ClientUtil.getClientById(clientId);
                    if(resultClient.isFailure()) {
                        throw new ApiException(resultClient.getError());
                    }
                    Map clientMap = JsonMapper.string2Map(resultClient.getResult());
                    if(!HashUtil.validatePassword(clientSecret.toCharArray(), (String)clientMap.get("clientSecret"))) {
                        throw new ApiException(new Status(UNAUTHORIZED_CLIENT));
                    }
                    result = clientId;
                }
            } catch (IOException | NoSuchAlgorithmException | InvalidKeySpecException e) {
                logger.error("Exception:", e);
                throw new ApiException(new Status(RUNTIME_EXCEPTION));
            }
        }
        return result;
    }


    private String getCertificateFromProvider(String providerId, String keyId) throws ClientException, ApiException {
        Result resultProvider = ClientUtil.getProviderDetail(providerId);
        String key = null;
        if(resultProvider.isSuccess()) {
            Map providerMap = JsonMapper.string2Map(resultProvider.getResult());
            KeyRequest keyRequest = new KeyRequest(keyId);
            keyRequest.setServerUrl((String)providerMap.get("serverUrl"));
            keyRequest.setUri(providerMap.get("uri") + "/00" + keyId);
            keyRequest.setEnableHttp2(true);
            key = OauthHelper.getKey(keyRequest);
        }
        return key;
    }

    private String getProviderId(String keyId)  {
        if ( keyId.length()< 4) {
            return null;
        } else {
            return keyId.substring(0,2);
        }
    }

    private String getKeyId(String keyId)  {
        if (keyId.length()< 4) {
            return keyId;
        } else {
            return keyId.substring(2);
        }
    }



    private void processAudit(HttpServerExchange exchange) throws Exception {
        if (config.isEnableAudit() ) {
            AuditInfo auditInfo = new AuditInfo();
            auditInfo.setServiceId(OauthService.OAUTH_KEY);
            auditInfo.setEndpoint(exchange.getHostName() + exchange.getRelativePath());
            auditInfo.setRequestHeader(exchange.getRequestHeaders().toString());
            auditInfo.setRequestBody(Config.getInstance().getMapper().writeValueAsString(exchange.getAttachment(BodyHandler.REQUEST_BODY)));
            auditInfo.setResponseCode(exchange.getStatusCode());
            auditInfo.setResponseHeader(exchange.getResponseHeaders().toString());
            auditInfo.setResponseBody(Config.getInstance().getMapper().writeValueAsString(exchange.getResponseCookies()));
            saveAudit(auditInfo);
        }
    }
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy