SslBundleHttpConduitConfigurer.java

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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 org.apache.cxf.spring.boot.autoconfigure.ssl;

import java.util.List;
import java.util.Objects;

import javax.net.ssl.SSLContext;

import org.apache.cxf.configuration.jsse.TLSClientParameters;
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.transport.http.HTTPConduitConfigurer;
import org.springframework.boot.ssl.SslBundle;
import org.springframework.boot.ssl.SslBundles;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;

/**
 * HTTPConduitConfigurer that applies a Spring Boot SslBundle to CXF HTTP clients. Bundle selection can be
 * global or by address pattern.
 */
final class SslBundleHttpConduitConfigurer implements HTTPConduitConfigurer {
    private final SslBundles sslBundles;
    private final CxfClientSslProperties props;
    private final AntPathMatcher matcher = new AntPathMatcher();

    SslBundleHttpConduitConfigurer(SslBundles sslBundles, CxfClientSslProperties props) {
        this.sslBundles = Objects.requireNonNull(sslBundles, "sslBundles");
        this.props = Objects.requireNonNull(props, "props");
    }

    @Override
    public void configure(String name, String address, HTTPConduit conduit) {
        CxfClientSslProperties.CxfClientSslBundle cxfClientSslBundle 
            = findMatchCxfClientSslBundle(address, props.getCxfClientSslBundles());
        String bundleName = cxfClientSslBundle != null ? cxfClientSslBundle.getBundle() : props.getDefaultBundle();
        if (!StringUtils.hasText(bundleName)) {
            return;
        }
        SslBundle bundle = sslBundles.getBundle(bundleName);
        TLSClientParameters tls = buildTls(bundle, cxfClientSslBundle);
        conduit.setTlsClientParameters(tls);
    }

    private TLSClientParameters buildTls(SslBundle bundle, 
                                         CxfClientSslProperties.CxfClientSslBundle cxfClientSslBundle) {
        SSLContext ctx = bundle.createSslContext();
        TLSClientParameters tls = new TLSClientParameters();
        tls.setSslContext(ctx);
        if (cxfClientSslBundle != null && StringUtils.hasText(cxfClientSslBundle.getProtocol())) {
            tls.setSecureSocketProtocol(cxfClientSslBundle.getProtocol());
        }
        if (cxfClientSslBundle != null 
            && cxfClientSslBundle.getCipherSuites() != null 
            && !cxfClientSslBundle.getCipherSuites().isEmpty()) {
            tls.setCipherSuites(cxfClientSslBundle.getCipherSuites());
        }
        if (cxfClientSslBundle != null) {
            tls.setDisableCNCheck(cxfClientSslBundle.getDisableCnCheck());
        } else {
            tls.setDisableCNCheck(props.getDisableCnCheck());
        }
        return tls;
    }

    private CxfClientSslProperties.CxfClientSslBundle findMatchCxfClientSslBundle(
                       String address, 
                       List<CxfClientSslProperties.CxfClientSslBundle> cxfClientSslBundles) {
        if (!StringUtils.hasText(address) || cxfClientSslBundles == null) {
            return null;
        }
        for (CxfClientSslProperties.CxfClientSslBundle r : cxfClientSslBundles) {
            String pat = r.getAddress();
            if (!StringUtils.hasText(pat)) {
                continue;
            }
            if (isPrefix(pat) && address.startsWith(pat)) {
                return r;
            }
            if (isAntStyle(pat) && matcher.match(pat, address)) {
                return r;
            }
        }
        return null;
    }

    private static boolean isPrefix(String p) {
        return p.startsWith("http://") || p.startsWith("https://");
    }

    private static boolean isAntStyle(String p) {
        return p.contains("*") || p.contains("?");
    }
}