blob: e27f91ccdc1c124edf5f1ed0a168ec2f9a184f83 [file] [log] [blame]
/*
* 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.nifi.web.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.security.cert.Certificate;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
import org.apache.nifi.security.util.CertificateUtils;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;
import com.sun.jersey.api.json.JSONConfiguration;
import com.sun.jersey.client.urlconnection.HTTPSProperties;
/**
* Common utilities related to web development.
*
*/
public final class WebUtils {
private static Logger logger = LoggerFactory.getLogger(WebUtils.class);
final static ReadWriteLock lock = new ReentrantReadWriteLock();
private WebUtils() {
}
/**
* Creates a client for non-secure requests. The client will be created
* using the given configuration. Additionally, the client will be
* automatically configured for JSON serialization/deserialization.
*
* @param config client configuration
*
* @return a Client instance
*/
public static Client createClient(final ClientConfig config) {
return createClientHelper(config, null);
}
/**
* Creates a client for secure requests. The client will be created using
* the given configuration and security context. Additionally, the client
* will be automatically configured for JSON serialization/deserialization.
*
* @param config client configuration
* @param ctx security context
*
* @return a Client instance
*/
public static Client createClient(final ClientConfig config, final SSLContext ctx) {
return createClientHelper(config, ctx);
}
/**
* A helper method for creating clients. The client will be created using
* the given configuration and security context. Additionally, the client
* will be automatically configured for JSON serialization/deserialization.
*
* @param config client configuration
* @param ctx security context, which may be null for non-secure client
* creation
*
* @return a Client instance
*/
private static Client createClientHelper(final ClientConfig config, final SSLContext ctx) {
final ClientConfig finalConfig = (config == null) ? new DefaultClientConfig() : config;
if (ctx != null && StringUtils.isBlank((String) finalConfig.getProperty(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES))) {
// custom hostname verifier that checks subject alternative names against the hostname of the URI
final HostnameVerifier hostnameVerifier = new HostnameVerifier() {
@Override
public boolean verify(final String hostname, final SSLSession ssls) {
try {
for (final Certificate peerCertificate : ssls.getPeerCertificates()) {
if (peerCertificate instanceof X509Certificate) {
final X509Certificate x509Cert = (X509Certificate) peerCertificate;
final List<String> subjectAltNames = CertificateUtils.getSubjectAlternativeNames(x509Cert);
if (subjectAltNames.contains(hostname.toLowerCase())) {
return true;
}
}
}
} catch (final SSLPeerUnverifiedException | CertificateParsingException ex) {
logger.warn("Hostname Verification encountered exception verifying hostname due to: " + ex, ex);
}
return false;
}
};
finalConfig.getProperties().put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES, new HTTPSProperties(hostnameVerifier, ctx));
}
finalConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
finalConfig.getClasses().add(ObjectMapperResolver.class);
// web client for restful request
return Client.create(finalConfig);
}
/**
* Serializes the given object to hexadecimal. Serialization uses Java's
* native serialization mechanism, the ObjectOutputStream.
*
* @param obj an object
* @return the serialized object as hex
*/
public static String serializeObjectToHex(final Serializable obj) {
final ByteArrayOutputStream serializedObj = new ByteArrayOutputStream();
// IOException can never be thrown because we are serializing to an in memory byte array
try {
final ObjectOutputStream oos = new ObjectOutputStream(serializedObj);
oos.writeObject(obj);
oos.close();
} catch (final IOException ioe) {
throw new RuntimeException(ioe);
}
logger.debug(String.format("Serialized object '%s' size: %d", obj, serializedObj.size()));
// hex encode the binary
return new String(Hex.encodeHex(serializedObj.toByteArray(), /* tolowercase */ true));
}
/**
* Deserializes a Java serialized, hex-encoded string into a Java object.
* This method is the inverse of the serializeObjectToHex method in this
* class.
*
* @param hexEncodedObject a string
* @return the object
* @throws ClassNotFoundException if the class could not be found
*/
public static Serializable deserializeHexToObject(final String hexEncodedObject) throws ClassNotFoundException {
// decode the hex encoded object
byte[] serializedObj;
try {
serializedObj = Hex.decodeHex(hexEncodedObject.toCharArray());
} catch (final DecoderException de) {
throw new IllegalArgumentException(de);
}
// IOException can never be thrown because we are deserializing from an in memory byte array
try {
// deserialize bytes into object
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(serializedObj));
return (Serializable) ois.readObject();
} catch (final IOException ioe) {
throw new RuntimeException(ioe);
}
}
}