mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
Merged HEAD-BUG-FIX (5.0/Cloud) to HEAD (5.0/Cloud)
84138: Merged FEATURE2 (5.0.0.FEATURE2) to HEAD-BUG-FIX (5.0/Cloud) 83970: ACE-2268 : make ldaps (LDAP over SSL) configurable using ldap subsystem properties and not only JVM (system) properties (JAVA_OPTS) Implemented the LDAPS truststore configuration via subsystem's properties. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@84634 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -102,6 +102,17 @@
|
||||
-->
|
||||
|
||||
<bean id="ldapInitialDirContextFactory" class="org.alfresco.repo.security.authentication.ldap.LDAPInitialDirContextFactoryImpl">
|
||||
|
||||
<property name="trustStorePath">
|
||||
<value>${ldap.authentication.truststore.path}</value>
|
||||
</property>
|
||||
<property name="trustStoreType">
|
||||
<value>${ldap.authentication.truststore.type}</value>
|
||||
</property>
|
||||
<property name="trustStorePassPhrase">
|
||||
<value>${ldap.authentication.truststore.passphrase}</value>
|
||||
</property>
|
||||
|
||||
<property name="initialDirContextEnvironment">
|
||||
<map>
|
||||
<!-- The LDAP provider -->
|
||||
@@ -133,6 +144,11 @@
|
||||
<entry key="java.naming.referral">
|
||||
<value>follow</value>
|
||||
</entry>
|
||||
|
||||
<!-- Set to 'ssl' to enable LDAPS configuration via subsystem's properties -->
|
||||
<entry key="java.naming.security.protocol">
|
||||
<value>${ldap.authentication.java.naming.security.protocol}</value>
|
||||
</entry>
|
||||
</map>
|
||||
</property>
|
||||
<property name="defaultIntialDirContextEnvironment">
|
||||
@@ -187,6 +203,11 @@
|
||||
<entry key="java.naming.referral">
|
||||
<value>follow</value>
|
||||
</entry>
|
||||
|
||||
<!-- Set to 'ssl' to enable LDAPS configuration via subsystem's properties -->
|
||||
<entry key="java.naming.security.protocol">
|
||||
<value>${ldap.authentication.java.naming.security.protocol}</value>
|
||||
</entry>
|
||||
</map>
|
||||
</property>
|
||||
</bean>
|
||||
|
@@ -124,3 +124,10 @@ ldap.synchronization.enableProgressEstimation=true
|
||||
|
||||
# Requests timeout, in miliseconds, use 0 for none (default)
|
||||
ldap.authentication.java.naming.read.timeout=0
|
||||
|
||||
# LDAPS truststore configuration properties
|
||||
#ldap.authentication.truststore.path=
|
||||
#ldap.authentication.truststore.passphrase=
|
||||
#ldap.authentication.truststore.type=
|
||||
# Set to 'ssl' to enable truststore configuration via subsystem's properties
|
||||
#ldap.authentication.java.naming.security.protocol=ssl
|
@@ -130,3 +130,10 @@ ldap.synchronization.enableProgressEstimation=true
|
||||
|
||||
# Requests timeout, in miliseconds, use 0 for none (default)
|
||||
ldap.authentication.java.naming.read.timeout=0
|
||||
|
||||
# LDAPS truststore configuration properties
|
||||
#ldap.authentication.truststore.path=
|
||||
#ldap.authentication.truststore.passphrase=
|
||||
#ldap.authentication.truststore.type=
|
||||
# Set to 'ssl' to enable truststore configuration via subsystem's properties
|
||||
#ldap.authentication.java.naming.security.protocol=ssl
|
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2014 Alfresco Software Limited.
|
||||
*
|
||||
* This file is part of Alfresco
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.alfresco.repo.security.authentication;
|
||||
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
|
||||
import javax.net.SocketFactory;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
/**
|
||||
* SSL socket factory that uses custom trustStore
|
||||
* <br>The factory should be first initialized
|
||||
*
|
||||
* @author alex.mukha
|
||||
* @since 5.0
|
||||
*/
|
||||
public class AlfrescoSSLSocketFactory extends SSLSocketFactory
|
||||
{
|
||||
private static SSLContext context;
|
||||
|
||||
public AlfrescoSSLSocketFactory()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the factory with custom trustStore
|
||||
* @param trustStore
|
||||
*/
|
||||
public static synchronized void initTrustedSSLSocketFactory(final KeyStore trustStore)
|
||||
{
|
||||
try
|
||||
{
|
||||
final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509");
|
||||
trustManagerFactory.init(trustStore);
|
||||
context = SSLContext.getInstance("SSL");
|
||||
context.init(null, trustManagerFactory.getTrustManagers(), SecureRandom.getInstance("SHA1PRNG"));
|
||||
}
|
||||
catch (NoSuchAlgorithmException nsae)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("The SSL socket factory cannot be initialized.", nsae);
|
||||
}
|
||||
catch (KeyStoreException kse)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("The SSL socket factory cannot be initialized.", kse);
|
||||
}
|
||||
catch (KeyManagementException kme)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("The SSL socket factory cannot be initialized.", kme);
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized SocketFactory getDefault()
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("The factory was not initialized.");
|
||||
}
|
||||
return new AlfrescoSSLSocketFactory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getDefaultCipherSuites()
|
||||
{
|
||||
return context.getSocketFactory().getDefaultCipherSuites();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSupportedCipherSuites()
|
||||
{
|
||||
return context.getSocketFactory().getSupportedCipherSuites();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(Socket socket, String s, int i, boolean b) throws IOException
|
||||
{
|
||||
return context.getSocketFactory().createSocket(socket, s, i, b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(String s, int i) throws IOException, UnknownHostException
|
||||
{
|
||||
return context.getSocketFactory().createSocket(s, i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(String s, int i, InetAddress inetAddress, int i2) throws IOException, UnknownHostException
|
||||
{
|
||||
return context.getSocketFactory().createSocket(s, i, inetAddress, i2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(InetAddress inetAddress, int i) throws IOException
|
||||
{
|
||||
return context.getSocketFactory().createSocket(inetAddress, i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(InetAddress inetAddress, int i, InetAddress inetAddress2, int i2) throws IOException
|
||||
{
|
||||
return context.getSocketFactory().createSocket(inetAddress, i, inetAddress2, i2);
|
||||
}
|
||||
}
|
@@ -18,7 +18,13 @@
|
||||
*/
|
||||
package org.alfresco.repo.security.authentication.ldap;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Hashtable;
|
||||
@@ -41,9 +47,12 @@ import javax.naming.ldap.LdapContext;
|
||||
import javax.naming.ldap.PagedResultsControl;
|
||||
import javax.naming.ldap.PagedResultsResponseControl;
|
||||
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.repo.security.authentication.AlfrescoSSLSocketFactory;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationDiagnostic;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationException;
|
||||
import org.alfresco.util.ApplicationContextHelper;
|
||||
import org.alfresco.util.PropertyCheck;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
@@ -58,6 +67,48 @@ public class LDAPInitialDirContextFactoryImpl implements LDAPInitialDirContextFa
|
||||
|
||||
private Map<String, String> defaultEnvironment = Collections.<String, String> emptyMap();
|
||||
private Map<String, String> authenticatedEnvironment = Collections.<String, String> emptyMap();
|
||||
private String trustStorePath;
|
||||
private String trustStoreType;
|
||||
private String trustStorePassPhrase;
|
||||
|
||||
public String getTrustStorePath()
|
||||
{
|
||||
return trustStorePath;
|
||||
}
|
||||
|
||||
public void setTrustStorePath(String trustStorePath)
|
||||
{
|
||||
if (PropertyCheck.isValidPropertyString(trustStorePath))
|
||||
{
|
||||
this.trustStorePath = trustStorePath;
|
||||
}
|
||||
}
|
||||
|
||||
public String getTrustStoreType()
|
||||
{
|
||||
return trustStoreType;
|
||||
}
|
||||
|
||||
public void setTrustStoreType(String trustStoreType)
|
||||
{
|
||||
if (PropertyCheck.isValidPropertyString(trustStoreType))
|
||||
{
|
||||
this.trustStoreType = trustStoreType;
|
||||
}
|
||||
}
|
||||
|
||||
public String getTrustStorePassPhrase()
|
||||
{
|
||||
return trustStorePassPhrase;
|
||||
}
|
||||
|
||||
public void setTrustStorePassPhrase(String trustStorePassPhrase)
|
||||
{
|
||||
if (PropertyCheck.isValidPropertyString(trustStorePassPhrase))
|
||||
{
|
||||
this.trustStorePassPhrase = trustStorePassPhrase;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
{
|
||||
@@ -114,6 +165,13 @@ public class LDAPInitialDirContextFactoryImpl implements LDAPInitialDirContextFa
|
||||
String securityPrincipal = env.get(Context.SECURITY_PRINCIPAL);
|
||||
String providerURL = env.get(Context.PROVIDER_URL);
|
||||
|
||||
if (isSSLSocketFactoryRequired())
|
||||
{
|
||||
KeyStore trustStore = initTrustStore();
|
||||
AlfrescoSSLSocketFactory.initTrustedSSLSocketFactory(trustStore);
|
||||
env.put("java.naming.ldap.factory.socket", AlfrescoSSLSocketFactory.class.getName());
|
||||
}
|
||||
|
||||
if(diagnostic == null)
|
||||
{
|
||||
diagnostic = new AuthenticationDiagnostic();
|
||||
@@ -392,6 +450,12 @@ public class LDAPInitialDirContextFactoryImpl implements LDAPInitialDirContextFa
|
||||
env.putAll(authenticatedEnvironment);
|
||||
env.remove(Context.SECURITY_PRINCIPAL);
|
||||
env.remove(Context.SECURITY_CREDENTIALS);
|
||||
if (isSSLSocketFactoryRequired())
|
||||
{
|
||||
KeyStore trustStore = initTrustStore();
|
||||
AlfrescoSSLSocketFactory.initTrustedSSLSocketFactory(trustStore);
|
||||
env.put("java.naming.ldap.factory.socket", AlfrescoSSLSocketFactory.class.getName());
|
||||
}
|
||||
try
|
||||
{
|
||||
new InitialDirContext(env);
|
||||
@@ -418,6 +482,12 @@ public class LDAPInitialDirContextFactoryImpl implements LDAPInitialDirContextFa
|
||||
env.putAll(authenticatedEnvironment);
|
||||
env.put(Context.SECURITY_PRINCIPAL, "daftAsABrush");
|
||||
env.put(Context.SECURITY_CREDENTIALS, "daftAsABrush");
|
||||
if (isSSLSocketFactoryRequired())
|
||||
{
|
||||
KeyStore trustStore = initTrustStore();
|
||||
AlfrescoSSLSocketFactory.initTrustedSSLSocketFactory(trustStore);
|
||||
env.put("java.naming.ldap.factory.socket", AlfrescoSSLSocketFactory.class.getName());
|
||||
}
|
||||
try
|
||||
{
|
||||
|
||||
@@ -447,6 +517,12 @@ public class LDAPInitialDirContextFactoryImpl implements LDAPInitialDirContextFa
|
||||
env.putAll(authenticatedEnvironment);
|
||||
env.put(Context.SECURITY_PRINCIPAL, "cn=daftAsABrush,dc=woof");
|
||||
env.put(Context.SECURITY_CREDENTIALS, "daftAsABrush");
|
||||
if (isSSLSocketFactoryRequired())
|
||||
{
|
||||
KeyStore trustStore = initTrustStore();
|
||||
AlfrescoSSLSocketFactory.initTrustedSSLSocketFactory(trustStore);
|
||||
env.put("java.naming.ldap.factory.socket", AlfrescoSSLSocketFactory.class.getName());
|
||||
}
|
||||
try
|
||||
{
|
||||
|
||||
@@ -481,6 +557,12 @@ public class LDAPInitialDirContextFactoryImpl implements LDAPInitialDirContextFa
|
||||
env.putAll(authenticatedEnvironment);
|
||||
env.put(Context.SECURITY_PRINCIPAL, principal);
|
||||
env.put(Context.SECURITY_CREDENTIALS, "sdasdasdasdasd123123123");
|
||||
if (isSSLSocketFactoryRequired())
|
||||
{
|
||||
KeyStore trustStore = initTrustStore();
|
||||
AlfrescoSSLSocketFactory.initTrustedSSLSocketFactory(trustStore);
|
||||
env.put("java.naming.ldap.factory.socket", AlfrescoSSLSocketFactory.class.getName());
|
||||
}
|
||||
if (!checkedEnvs.contains(env))
|
||||
{
|
||||
|
||||
@@ -513,7 +595,80 @@ public class LDAPInitialDirContextFactoryImpl implements LDAPInitialDirContextFa
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Check if it required to use custom SSL socket factory with custom trustStore.
|
||||
* <br>Required for LDAPS configuration. The <code>ldap.authentication.java.naming.security.protocol</code> should be set to "ssl" for LDAPS.
|
||||
* <br>The following properties should be set:
|
||||
* <ul>
|
||||
* <li>ldap.authentication.truststore.path
|
||||
* <li>ldap.authentication.truststore.type
|
||||
* <li>ldap.authentication.truststore.passphrase
|
||||
* <li>ldap.authentication.java.naming.security.protocol
|
||||
* </ul>
|
||||
*
|
||||
* @return <code>true</code> if all the required properties are set
|
||||
*/
|
||||
private boolean isSSLSocketFactoryRequired()
|
||||
{
|
||||
boolean result = false;
|
||||
// Check for LDAPS config
|
||||
String protocol = authenticatedEnvironment.get(Context.SECURITY_PROTOCOL);
|
||||
if (protocol != null && protocol.equals("ssl"))
|
||||
{
|
||||
if (getTrustStoreType() != null && getTrustStorePath() != null && getTrustStoreType() != null)
|
||||
{
|
||||
result = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.warn("The SSL configuration for LDAPS is not full, the default configuration will be used.");
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize trustStore with Spring set properties:
|
||||
* <ul>
|
||||
* <li>ldap.authentication.truststore.path
|
||||
* <li>ldap.authentication.truststore.type
|
||||
* <li>ldap.authentication.truststore.passphrase
|
||||
* </ul>
|
||||
*
|
||||
* @return {@link KeyStore} with loaded trustStore file
|
||||
*/
|
||||
private KeyStore initTrustStore()
|
||||
{
|
||||
KeyStore ks;
|
||||
String trustStoreType = getTrustStoreType();
|
||||
try
|
||||
{
|
||||
ks = KeyStore.getInstance(trustStoreType);
|
||||
}
|
||||
catch (KeyStoreException kse)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("No provider supports " + trustStoreType, kse);
|
||||
}
|
||||
try
|
||||
{
|
||||
ks.load(new FileInputStream(getTrustStorePath()), getTrustStorePassPhrase().toCharArray());
|
||||
}
|
||||
catch (FileNotFoundException fnfe)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("The truststore file is not found.", fnfe);
|
||||
}
|
||||
catch (IOException ioe)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("The truststore file cannot be read.", ioe);
|
||||
}
|
||||
catch (NoSuchAlgorithmException nsae)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Algorithm used to check the integrity of the truststore cannot be found.", nsae);
|
||||
}
|
||||
catch (CertificateException ce)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("The certificates cannot be loaded from truststore.", ce);
|
||||
}
|
||||
return ks;
|
||||
}
|
||||
}
|
||||
|
@@ -23,6 +23,7 @@ import junit.framework.Test;
|
||||
import junit.framework.TestSuite;
|
||||
|
||||
import org.alfresco.repo.ownable.impl.OwnableServiceTest;
|
||||
import org.alfresco.repo.security.authentication.AlfrescoSSLSocketFactoryTest;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationBootstrapTest;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationTest;
|
||||
import org.alfresco.repo.security.authentication.AuthorizationTest;
|
||||
@@ -81,7 +82,7 @@ public class SecurityTestSuite extends TestSuite
|
||||
suite.addTestSuite(AuthorityBridgeTableAsynchronouslyRefreshedCacheTest.class);
|
||||
|
||||
suite.addTest(new JUnit4TestAdapter(HomeFolderProviderSynchronizerTest.class));
|
||||
|
||||
suite.addTest(new JUnit4TestAdapter(AlfrescoSSLSocketFactoryTest.class));
|
||||
return suite;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2014 Alfresco Software Limited.
|
||||
*
|
||||
* This file is part of Alfresco
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.alfresco.repo.security.authentication;
|
||||
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.security.KeyStore;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* SSL socket factory test
|
||||
*
|
||||
* @author alex.mukha
|
||||
* @since 5.0
|
||||
*/
|
||||
public class AlfrescoSSLSocketFactoryTest
|
||||
{
|
||||
private static final String KEYSTORE_TYPE = "JCEKS";
|
||||
|
||||
@Test
|
||||
public void testConfiguration() throws Exception
|
||||
{
|
||||
KeyStore ks = KeyStore.getInstance(KEYSTORE_TYPE);
|
||||
// try to use the factory without initialization
|
||||
try
|
||||
{
|
||||
AlfrescoSSLSocketFactory.getDefault();
|
||||
fail("An AlfrescoRuntimeException should be thrown as the factory is not initialized.");
|
||||
}
|
||||
catch (AlfrescoRuntimeException are)
|
||||
{
|
||||
// Expected
|
||||
}
|
||||
|
||||
// initialize and get an instance of AlfrescoSSLSocketFactory
|
||||
AlfrescoSSLSocketFactory.initTrustedSSLSocketFactory(ks);
|
||||
AlfrescoSSLSocketFactory.getDefault();
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user