/*
* ToroDB
* Copyright © 2014 8Kdata Technology (www.8kdata.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.torodb.packaging.util;
import com.eightkdata.mongowp.client.wrapper.MongoAuthenticationConfiguration;
import com.eightkdata.mongowp.client.wrapper.MongoAuthenticationMechanism;
import com.eightkdata.mongowp.client.wrapper.MongoClientConfiguration;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.common.net.HostAndPort;
import com.torodb.core.exceptions.SystemException;
import com.torodb.packaging.config.model.protocol.mongo.AbstractReplication;
import com.torodb.packaging.config.model.protocol.mongo.Auth;
import com.torodb.packaging.config.model.protocol.mongo.AuthMode;
import com.torodb.packaging.config.model.protocol.mongo.Ssl;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
public class MongoClientConfigurationFactory {
@SuppressWarnings("checkstyle:LineLength")
private static final ImmutableMap<AuthMode, Function<AuthMode, MongoAuthenticationMechanism>> mongoAuthenticationMechanismConverter =
Maps.immutableEnumMap(ImmutableMap.of(
AuthMode.cr, a -> MongoAuthenticationMechanism.cr,
AuthMode.scram_sha1, a -> MongoAuthenticationMechanism.scram_sha1,
AuthMode.negotiate, a -> MongoAuthenticationMechanism.negotiate,
AuthMode.x509, a -> MongoAuthenticationMechanism.x509
));
public static MongoClientConfiguration getMongoClientConfiguration(
AbstractReplication replication) {
HostAndPort syncSource = HostAndPort.fromString(replication.getSyncSource())
.withDefaultPort(27017);
MongoClientConfiguration.Builder mongoClientConfigurationBuilder =
new MongoClientConfiguration.Builder(syncSource);
Ssl ssl = replication.getSsl();
mongoClientConfigurationBuilder.setSslEnabled(ssl.getEnabled());
if (ssl.getEnabled()) {
try {
mongoClientConfigurationBuilder.setSslAllowInvalidHostnames(ssl.getAllowInvalidHostnames());
TrustManager[] tms = getTrustManagers(ssl);
KeyManager[] kms = getKeyManagers(ssl);
SSLContext sslContext;
if (ssl.getFipsMode()) {
sslContext = SSLContext.getInstance("TLS", "SunPKCS11-NSS");
} else {
sslContext = SSLContext.getInstance("TLS");
}
sslContext.init(kms, tms, null);
mongoClientConfigurationBuilder.setSocketFactory(sslContext.getSocketFactory());
} catch (CertificateException | KeyManagementException | KeyStoreException
| UnrecoverableKeyException | NoSuchProviderException | NoSuchAlgorithmException
| IOException exception) {
throw new SystemException(exception);
}
}
Auth auth = replication.getAuth();
if (auth.getMode().isEnabled()) {
MongoAuthenticationConfiguration mongoAuthenticationConfiguration =
getMongoAuthenticationConfiguration(auth, ssl);
mongoClientConfigurationBuilder.addAuthenticationConfiguration(
mongoAuthenticationConfiguration);
}
return mongoClientConfigurationBuilder.build();
}
public static TrustManager[] getTrustManagers(Ssl ssl) throws NoSuchAlgorithmException,
FileNotFoundException,
CertificateException, KeyStoreException, IOException {
TrustManager[] tms = null;
if (ssl.getCaFile() != null) {
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory
.getDefaultAlgorithm());
try (InputStream is = new FileInputStream(ssl.getCaFile())) {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate caCert = (X509Certificate) cf.generateCertificate(is);
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null);
ks.setCertificateEntry("ca", caCert);
tmf.init(ks);
tms = tmf.getTrustManagers();
}
} else if (ssl.getTrustStoreFile() != null) {
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory
.getDefaultAlgorithm());
try (InputStream is = new FileInputStream(ssl.getCaFile())) {
char[] storePassword = null;
if (ssl.getTrustStorePassword() != null) {
storePassword = ssl.getTrustStorePassword().toCharArray();
}
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(is, storePassword);
tmf.init(ks);
tms = tmf.getTrustManagers();
}
}
return tms;
}
public static KeyManager[] getKeyManagers(Ssl ssl) throws NoSuchAlgorithmException,
FileNotFoundException,
KeyStoreException, IOException, CertificateException, UnrecoverableKeyException {
KeyManager[] kms = null;
if (ssl.getKeyStoreFile() != null) {
KeyManagerFactory kmf = KeyManagerFactory.getInstance(
KeyManagerFactory.getDefaultAlgorithm());
KeyStore ks = getKeyStore(ssl);
char[] keyPassword = null;
if (ssl.getKeyPassword() != null) {
keyPassword = ssl.getKeyPassword().toCharArray();
}
kmf.init(ks, keyPassword);
kms = kmf.getKeyManagers();
}
return kms;
}
private static KeyStore getKeyStore(Ssl ssl) throws FileNotFoundException, KeyStoreException,
IOException,
NoSuchAlgorithmException, CertificateException {
try (InputStream is = new FileInputStream(ssl.getKeyStoreFile())) {
char[] storePassword = null;
if (ssl.getKeyStorePassword() != null) {
storePassword = ssl.getKeyStorePassword().toCharArray();
}
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(is, storePassword);
return ks;
}
}
private static MongoAuthenticationConfiguration getMongoAuthenticationConfiguration(Auth auth,
Ssl ssl) {
AuthMode authMode = auth.getMode();
MongoAuthenticationConfiguration.Builder mongoAuthenticationConfigurationBuilder =
new MongoAuthenticationConfiguration.Builder(mongoAuthenticationMechanismConverter.get(
authMode).apply(authMode));
mongoAuthenticationConfigurationBuilder.setUser(auth.getUser());
mongoAuthenticationConfigurationBuilder.setSource(auth.getSource());
mongoAuthenticationConfigurationBuilder.setPassword(auth.getPassword());
if (authMode == AuthMode.x509 && auth.getUser() == null) {
try {
KeyStore ks = getKeyStore(ssl);
X509Certificate certificate = (X509Certificate) ks
.getCertificate(ks.aliases().nextElement());
mongoAuthenticationConfigurationBuilder.setUser(
Arrays.asList(certificate.getSubjectDN().getName().split(",")).stream()
.map(dn -> dn.trim()).collect(Collectors.joining(",")));
} catch (CertificateException | KeyStoreException | NoSuchAlgorithmException
| IOException exception) {
throw new SystemException(exception);
}
}
return mongoAuthenticationConfigurationBuilder.build();
}
}