From commits-return-52594-archive-asf-public=cust-asf.ponee.io@directory.apache.org Fri Jun 4 17:53:01 2021 Return-Path: X-Original-To: archive-asf-public@cust-asf.ponee.io Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mxout1-ec2-va.apache.org (mxout1-ec2-va.apache.org [3.227.148.255]) by mx-eu-01.ponee.io (Postfix) with ESMTPS id E582618066B for ; Fri, 4 Jun 2021 19:53:00 +0200 (CEST) Received: from mail.apache.org (mailroute1-lw-us.apache.org [207.244.88.153]) by mxout1-ec2-va.apache.org (ASF Mail Server at mxout1-ec2-va.apache.org) with SMTP id 263053EB5A for ; Fri, 4 Jun 2021 17:53:00 +0000 (UTC) Received: (qmail 63762 invoked by uid 500); 4 Jun 2021 17:52:59 -0000 Mailing-List: contact commits-help@directory.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@directory.apache.org Delivered-To: mailing list commits@directory.apache.org Received: (qmail 63753 invoked by uid 99); 4 Jun 2021 17:52:59 -0000 Received: from ec2-52-202-80-70.compute-1.amazonaws.com (HELO gitbox.apache.org) (52.202.80.70) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 04 Jun 2021 17:52:59 +0000 Received: by gitbox.apache.org (ASF Mail Server at gitbox.apache.org, from userid 33) id 9395681A86; Fri, 4 Jun 2021 17:52:59 +0000 (UTC) Date: Fri, 04 Jun 2021 17:52:59 +0000 To: "commits@directory.apache.org" Subject: [directory-studio] branch master updated: DIRSTUDIO-1219: Ensure StartTLS on connect MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Message-ID: <162282917951.27030.7448837098202991972@gitbox.apache.org> From: seelmann@apache.org X-Git-Host: gitbox.apache.org X-Git-Repo: directory-studio X-Git-Refname: refs/heads/master X-Git-Reftype: branch X-Git-Oldrev: 8c758465c5a3e96e921e2b0083f85d025e16799f X-Git-Newrev: b53667ab3b87afcfcd6f1b1df90d733636cfc888 X-Git-Rev: b53667ab3b87afcfcd6f1b1df90d733636cfc888 X-Git-NotificationType: ref_changed_plus_diff X-Git-Multimail-Version: 1.5.dev Auto-Submitted: auto-generated This is an automated email from the ASF dual-hosted git repository. seelmann pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/directory-studio.git The following commit(s) were added to refs/heads/master by this push: new b53667a DIRSTUDIO-1219: Ensure StartTLS on connect b53667a is described below commit b53667ab3b87afcfcd6f1b1df90d733636cfc888 Author: Stefan Seelmann AuthorDate: Fri Jun 4 19:49:56 2021 +0200 DIRSTUDIO-1219: Ensure StartTLS on connect * Call startTls() right after connect() * Change icon and label to indicate that connection is secured * Remove unused SASL Plain mechanism and cleanup related code * Add several test scenarios --- .../connection/core/ConnectionParameter.java | 25 +- .../directory/studio/connection/core/Messages.java | 1 + .../connection/core/io/ConnectionWrapper.java | 10 +- .../core/io/api/DirectoryApiConnectionWrapper.java | 98 ++-- .../studio/connection/core/messages.properties | 1 + .../studio/connection/core/messages_de.properties | 1 + .../studio/connection/core/messages_fr.properties | 1 + .../ui/widgets/AuthenticationParameterPage.java | 27 - .../ui/widgets/ConnectionLabelProvider.java | 23 +- .../core/DirectoryApiConnectionWrapperTest.java | 370 ++++++++++--- .../integration/junit5/ApacheDirectoryServer.java | 10 +- .../integration/junit5/Fedora389dsLdapServer.java | 15 + .../test/integration/junit5/OpenLdapServer.java | 28 +- .../test/integration/junit5/TestLdapServer.java | 22 +- .../test/integration/junit5/OpenLdapConfig.ldif | 3 +- .../test/integration/ui/AbstractTestBase.java | 16 + .../integration/ui/CertificateValidationTest.java | 14 - .../integration/ui/NewConnectionWizardTest.java | 607 +++++++++++++++------ .../studio/test/integration/ui/bots/WizardBot.java | 7 + 19 files changed, 904 insertions(+), 375 deletions(-) diff --git a/plugins/connection.core/src/main/java/org/apache/directory/studio/connection/core/ConnectionParameter.java b/plugins/connection.core/src/main/java/org/apache/directory/studio/connection/core/ConnectionParameter.java index caa01df..6ef9acf 100644 --- a/plugins/connection.core/src/main/java/org/apache/directory/studio/connection/core/ConnectionParameter.java +++ b/plugins/connection.core/src/main/java/org/apache/directory/studio/connection/core/ConnectionParameter.java @@ -55,6 +55,13 @@ public class ConnectionParameter /** Encryption using Start TLS extension. */ START_TLS + + ; + + public boolean isEncrytped() + { + return this != NONE; + } } /** @@ -77,19 +84,16 @@ public class ConnectionParameter SASL_CRAM_MD5(3), /** SASL authentication using GSSAPI. */ - SASL_GSSAPI(4), - - /** SASL PLAIN authentication */ - SASL_PLAIN(5); - - + SASL_GSSAPI(4); + private int value; - + private AuthenticationMethod( int value ) { this.value = value; } + public int getValue() { return value; @@ -165,11 +169,10 @@ public class ConnectionParameter /** The extended properties. */ private Map extendedProperties; - + /** The connection timeout. Default to 30 seconds */ private long timeoutMillis = 30000L; - /** * Creates a new instance of ConnectionParameter. */ @@ -756,7 +759,7 @@ public class ConnectionParameter public int getExtendedIntProperty( String key ) { String s = extendedProperties.get( key ); - + if ( s != null ) { return Integer.parseInt( s ); @@ -790,7 +793,7 @@ public class ConnectionParameter public boolean getExtendedBoolProperty( String key ) { String s = extendedProperties.get( key ); - + if ( s != null ) { return Boolean.parseBoolean( s ); diff --git a/plugins/connection.core/src/main/java/org/apache/directory/studio/connection/core/Messages.java b/plugins/connection.core/src/main/java/org/apache/directory/studio/connection/core/Messages.java index 865636c..9f6bd9c 100644 --- a/plugins/connection.core/src/main/java/org/apache/directory/studio/connection/core/Messages.java +++ b/plugins/connection.core/src/main/java/org/apache/directory/studio/connection/core/Messages.java @@ -89,5 +89,6 @@ public class Messages extends NLS public static String DirectoryApiConnectionWrapper_NoConnection; public static String DirectoryApiConnectionWrapper_UnableToConnect; + public static String DirectoryApiConnectionWrapper_UnsecuredConnection; } diff --git a/plugins/connection.core/src/main/java/org/apache/directory/studio/connection/core/io/ConnectionWrapper.java b/plugins/connection.core/src/main/java/org/apache/directory/studio/connection/core/io/ConnectionWrapper.java index b92b122..7c7d1ff 100644 --- a/plugins/connection.core/src/main/java/org/apache/directory/studio/connection/core/io/ConnectionWrapper.java +++ b/plugins/connection.core/src/main/java/org/apache/directory/studio/connection/core/io/ConnectionWrapper.java @@ -32,9 +32,9 @@ import org.apache.directory.api.ldap.model.message.ExtendedRequest; import org.apache.directory.api.ldap.model.message.ExtendedResponse; import org.apache.directory.api.ldap.model.name.Dn; import org.apache.directory.studio.common.core.jobs.StudioProgressMonitor; -import org.apache.directory.studio.connection.core.ReferralsInfo; import org.apache.directory.studio.connection.core.Connection.AliasDereferencingMethod; import org.apache.directory.studio.connection.core.Connection.ReferralHandlingMethod; +import org.apache.directory.studio.connection.core.ReferralsInfo; import org.apache.directory.studio.connection.core.io.api.StudioSearchResultEnumeration; @@ -82,6 +82,14 @@ public interface ConnectionWrapper /** + * Checks if the connection is secured. + * + * @return true, if is secured + */ + boolean isSecured(); + + + /** * Sets the binary attributes. * * @param binaryAttributes the binary attributes diff --git a/plugins/connection.core/src/main/java/org/apache/directory/studio/connection/core/io/api/DirectoryApiConnectionWrapper.java b/plugins/connection.core/src/main/java/org/apache/directory/studio/connection/core/io/api/DirectoryApiConnectionWrapper.java index 949fe30..8c7c694 100644 --- a/plugins/connection.core/src/main/java/org/apache/directory/studio/connection/core/io/api/DirectoryApiConnectionWrapper.java +++ b/plugins/connection.core/src/main/java/org/apache/directory/studio/connection/core/io/api/DirectoryApiConnectionWrapper.java @@ -76,7 +76,6 @@ import org.apache.directory.ldap.client.api.LdapNetworkConnection; import org.apache.directory.ldap.client.api.SaslCramMd5Request; import org.apache.directory.ldap.client.api.SaslDigestMd5Request; import org.apache.directory.ldap.client.api.SaslGssApiRequest; -import org.apache.directory.ldap.client.api.SaslPlainRequest; import org.apache.directory.ldap.client.api.exception.InvalidConnectionException; import org.apache.directory.studio.common.core.jobs.StudioProgressMonitor; import org.apache.directory.studio.connection.core.Connection; @@ -109,33 +108,21 @@ public class DirectoryApiConnectionWrapper implements ConnectionWrapper /** The search request number */ private static int searchRequestNum = 0; - /** The connection*/ + /** The Studio connection */ private Connection connection; - /** The LDAP Connection Configuration */ + /** The LDAP connection configuration */ private LdapConnectionConfig ldapConnectionConfig; - /** The LDAP Connection */ + /** The LDAP connection */ private LdapNetworkConnection ldapConnection; /** The binary attribute detector */ private DefaultConfigurableBinaryAttributeDetector binaryAttributeDetector; - /** Indicates if the wrapper is connected */ - private boolean isConnected = false; - /** The current job thread */ private Thread jobThread; - /** The bind principal */ - private String bindPrincipal; - - /** The bind password */ - private String bindPassword; - - /** The SASL PLAIN authzid */ - private String authzId; - /** * Creates a new instance of DirectoryApiConnectionWrapper. * @@ -153,7 +140,6 @@ public class DirectoryApiConnectionWrapper implements ConnectionWrapper public X509Certificate[] connect( StudioProgressMonitor monitor ) { ldapConnection = null; - isConnected = false; jobThread = null; try @@ -172,7 +158,6 @@ public class DirectoryApiConnectionWrapper implements ConnectionWrapper private X509Certificate[] doConnect( final StudioProgressMonitor monitor ) throws Exception { ldapConnection = null; - isConnected = true; ldapConnectionConfig = new LdapConnectionConfig(); ldapConnectionConfig.setLdapHost( connection.getHost() ); @@ -231,6 +216,12 @@ public class DirectoryApiConnectionWrapper implements ConnectionWrapper { public void run() { + /* + * Use local temp variable while the connection is being established and secured. + * This process can take a while and the user might be asked to inspect the server + * certificate. During that process the connection must not be used. + */ + LdapNetworkConnection ldapConnectionUnderConstruction = null; try { // Set lower timeout for connecting @@ -238,13 +229,13 @@ public class DirectoryApiConnectionWrapper implements ConnectionWrapper ldapConnectionConfig.setTimeout( Math.min( oldTimeout, 5000L ) ); // Connecting - ldapConnection = new LdapNetworkConnection( ldapConnectionConfig ); - boolean connected = ldapConnection.connect(); + ldapConnectionUnderConstruction = new LdapNetworkConnection( ldapConnectionConfig ); + ldapConnectionUnderConstruction.connect(); - // Establish TLS layer if TLS is enabled and SSL is not + // DIRSTUDIO-1219: Establish TLS layer if TLS is enabled and SSL is not if ( ldapConnectionConfig.isUseTls() && !ldapConnectionConfig.isUseSsl() ) { - ldapConnection.startTls(); + ldapConnectionUnderConstruction.startTls(); } // Capture the server certificates @@ -253,11 +244,23 @@ public class DirectoryApiConnectionWrapper implements ConnectionWrapper serverCertificates.set( studioTrustmanager.get().getChain() ); } - if ( !connected ) + // Now set the LDAP connection once the (optional) security layer is in place + ldapConnection = ldapConnectionUnderConstruction; + + if ( !isConnected() ) { throw new Exception( Messages.DirectoryApiConnectionWrapper_UnableToConnect ); } + // DIRSTUDIO-1219: Verify secure connection if ldaps:// or StartTLS is configured + if ( ldapConnectionConfig.isUseTls() || ldapConnectionConfig.isUseSsl() ) + { + if ( !isSecured() || serverCertificates.get() == null ) + { + throw new Exception( Messages.DirectoryApiConnectionWrapper_UnsecuredConnection ); + } + } + // Set old timeout again ldapConnectionConfig.setTimeout( oldTimeout ); } @@ -267,9 +270,9 @@ public class DirectoryApiConnectionWrapper implements ConnectionWrapper try { - if ( ldapConnection != null ) + if ( ldapConnectionUnderConstruction != null ) { - ldapConnection.close(); + ldapConnectionUnderConstruction.close(); } } catch ( Exception exception ) @@ -320,7 +323,6 @@ public class DirectoryApiConnectionWrapper implements ConnectionWrapper ldapConnection = null; binaryAttributeDetector = null; } - isConnected = false; } @@ -351,26 +353,9 @@ public class DirectoryApiConnectionWrapper implements ConnectionWrapper } - private BindResponse bindSaslPlain() throws LdapException - { - SaslPlainRequest saslPlainRequest = new SaslPlainRequest(); - saslPlainRequest.setUsername( bindPrincipal ); - saslPlainRequest.setCredentials( bindPassword ); - saslPlainRequest.setAuthorizationId( authzId ); - saslPlainRequest - .setQualityOfProtection( connection.getConnectionParameter().getSaslQop() ); - saslPlainRequest.setSecurityStrength( connection.getConnectionParameter() - .getSaslSecurityStrength() ); - saslPlainRequest.setMutualAuthentication( connection.getConnectionParameter() - .isSaslMutualAuthentication() ); - - return ldapConnection.bindSaslPlain( bindPrincipal, bindPassword, authzId ); - } - - private void doBind( final StudioProgressMonitor monitor ) throws Exception { - if ( ldapConnection != null && isConnected ) + if ( isConnected() ) { InnerRunnable runnable = new InnerRunnable() { @@ -413,21 +398,14 @@ public class DirectoryApiConnectionWrapper implements ConnectionWrapper monitor.reportError( Messages.model__no_credentials, exception ); throw exception; } - bindPrincipal = credentials.getBindPrincipal(); - bindPassword = credentials.getBindPassword(); + String bindPrincipal = credentials.getBindPrincipal(); + String bindPassword = credentials.getBindPassword(); switch ( connection.getConnectionParameter().getAuthMethod() ) { case SIMPLE: // Simple Authentication bindResponse = bindSimple( bindPrincipal, bindPassword ); - - break; - - case SASL_PLAIN: - // SASL Plain authentication - bindResponse = bindSaslPlain(); - break; case SASL_CRAM_MD5: @@ -555,6 +533,15 @@ public class DirectoryApiConnectionWrapper implements ConnectionWrapper /** * {@inheritDoc} */ + public boolean isSecured() + { + return isConnected() && ldapConnection.isSecured(); + } + + + /** + * {@inheritDoc} + */ public void setBinaryAttributes( Collection binaryAttributes ) { if ( binaryAttributeDetector != null ) @@ -1212,7 +1199,7 @@ public class DirectoryApiConnectionWrapper implements ConnectionWrapper throws Exception { // check connection - if ( !isConnected || ldapConnection == null ) + if ( !isConnected() ) { doConnect( monitor ); doBind( monitor ); @@ -1266,11 +1253,8 @@ public class DirectoryApiConnectionWrapper implements ConnectionWrapper { } - isConnected = false; ldapConnection = null; } - - isConnected = false; } }; diff --git a/plugins/connection.core/src/main/java/org/apache/directory/studio/connection/core/messages.properties b/plugins/connection.core/src/main/java/org/apache/directory/studio/connection/core/messages.properties index 5619285..aafd382 100644 --- a/plugins/connection.core/src/main/java/org/apache/directory/studio/connection/core/messages.properties +++ b/plugins/connection.core/src/main/java/org/apache/directory/studio/connection/core/messages.properties @@ -56,3 +56,4 @@ StudioKeyStoreManager_CantRemoveCertificateFromTrustStore=Can't remove certifica DirectoryApiConnectionWrapper_NoConnection=No Connection DirectoryApiConnectionWrapper_UnableToConnect=Unable to connect +DirectoryApiConnectionWrapper_UnsecuredConnection=Unsecured connection diff --git a/plugins/connection.core/src/main/java/org/apache/directory/studio/connection/core/messages_de.properties b/plugins/connection.core/src/main/java/org/apache/directory/studio/connection/core/messages_de.properties index ad95505..cb40eb0 100644 --- a/plugins/connection.core/src/main/java/org/apache/directory/studio/connection/core/messages_de.properties +++ b/plugins/connection.core/src/main/java/org/apache/directory/studio/connection/core/messages_de.properties @@ -58,3 +58,4 @@ StudioKeyStoreManager_CantRemoveCertificateFromTrustStore=Fehler beim L\u00FCsch DirectoryApiConnectionWrapper_NoConnection=Keine Verbindung DirectoryApiConnectionWrapper_UnableToConnect=Verbindung nicht m�glich +DirectoryApiConnectionWrapper_UnsecuredConnection=Unsichere Verbindung diff --git a/plugins/connection.core/src/main/java/org/apache/directory/studio/connection/core/messages_fr.properties b/plugins/connection.core/src/main/java/org/apache/directory/studio/connection/core/messages_fr.properties index ba7e52b..74cd0fa 100644 --- a/plugins/connection.core/src/main/java/org/apache/directory/studio/connection/core/messages_fr.properties +++ b/plugins/connection.core/src/main/java/org/apache/directory/studio/connection/core/messages_fr.properties @@ -58,3 +58,4 @@ StudioKeyStoreManager_CantRemoveCertificateFromTrustStore=Impossible de supprime DirectoryApiConnectionWrapper_NoConnection=Pas de connexion DirectoryApiConnectionWrapper_UnableToConnect=Impossible de se connecter +DirectoryApiConnectionWrapper_UnsecuredConnection=Connexion non s\u00E9curis\u00E9e diff --git a/plugins/connection.ui/src/main/java/org/apache/directory/studio/connection/ui/widgets/AuthenticationParameterPage.java b/plugins/connection.ui/src/main/java/org/apache/directory/studio/connection/ui/widgets/AuthenticationParameterPage.java index 87842a5..a82cb5d 100644 --- a/plugins/connection.ui/src/main/java/org/apache/directory/studio/connection/ui/widgets/AuthenticationParameterPage.java +++ b/plugins/connection.ui/src/main/java/org/apache/directory/studio/connection/ui/widgets/AuthenticationParameterPage.java @@ -84,9 +84,6 @@ public class AuthenticationParameterPage extends AbstractConnectionParameterPage /** The URL simple constant */ private static final String X_AUTH_METHOD_SIMPLE = "Simple"; //$NON-NLS-1$ - /** The URL PLAIN constant */ - private static final String X_AUTH_METHOD_PLAIN = "PLAIN"; //$NON-NLS-1$ - /** The URL DIGEST-MD5 constant */ private static final String X_AUTH_METHOD_DIGEST_MD5 = "DIGEST-MD5"; //$NON-NLS-1$ @@ -145,9 +142,6 @@ public class AuthenticationParameterPage extends AbstractConnectionParameterPage /** The text widget to input bind password */ private Text bindPasswordText; - /** The text widget to input the SASL PLAIN autzid (if selected) */ - private Text authzidText; - /** The checkbox to choose if the bind password should be saved on disk */ private Button saveBindPasswordButton; @@ -173,7 +167,6 @@ public class AuthenticationParameterPage extends AbstractConnectionParameterPage private Text krb5ConfigManualHostText; private Text krb5ConfigManualPortText; - /** * Gets the authentication method. * @@ -223,17 +216,6 @@ public class AuthenticationParameterPage extends AbstractConnectionParameterPage } - /** - * Gets the bind authzid. - * - * @return the authzid - */ - private String getAuthzid() - { - return authzidText.getText(); - } - - private String getSaslRealm() { return saslRealmText.getText(); @@ -378,10 +360,6 @@ public class AuthenticationParameterPage extends AbstractConnectionParameterPage ConnectionUIConstants.DIALOGSETTING_KEY_PRINCIPAL_HISTORY ); bindPrincipalCombo = BaseWidgetUtils.createCombo( composite, dnHistory, -1, 2 ); - BaseWidgetUtils.createLabel( composite, Messages.getString( "AuthenticationParameterPage.Authzid" ), 1 ); //$NON-NLS-1$ - authzidText = BaseWidgetUtils.createText( composite, "SASL PLAIN only", 2 ); //$NON-NLS-1$ - authzidText.setEnabled( false ); - BaseWidgetUtils.createLabel( composite, Messages.getString( "AuthenticationParameterPage.BindPassword" ), 1 ); //$NON-NLS-1$ bindPasswordText = BaseWidgetUtils.createPasswordText( composite, StringUtils.EMPTY, 2 ); //$NON-NLS-1$ @@ -1031,10 +1009,6 @@ public class AuthenticationParameterPage extends AbstractConnectionParameterPage { switch ( parameter.getAuthMethod() ) { - case SASL_PLAIN : - ldapUrl.getExtensions().add( new Extension( false, X_AUTH_METHOD, X_AUTH_METHOD_PLAIN ) ); - break; - case SASL_CRAM_MD5: ldapUrl.getExtensions().add( new Extension( false, X_AUTH_METHOD, X_AUTH_METHOD_CRAM_MD5 ) ); break; @@ -1078,7 +1052,6 @@ public class AuthenticationParameterPage extends AbstractConnectionParameterPage switch ( parameter.getAuthMethod() ) { - case SASL_PLAIN: case SASL_CRAM_MD5: case SASL_DIGEST_MD5: case SASL_GSSAPI: diff --git a/plugins/connection.ui/src/main/java/org/apache/directory/studio/connection/ui/widgets/ConnectionLabelProvider.java b/plugins/connection.ui/src/main/java/org/apache/directory/studio/connection/ui/widgets/ConnectionLabelProvider.java index 4b6f2eb..b1e4831 100644 --- a/plugins/connection.ui/src/main/java/org/apache/directory/studio/connection/ui/widgets/ConnectionLabelProvider.java +++ b/plugins/connection.ui/src/main/java/org/apache/directory/studio/connection/ui/widgets/ConnectionLabelProvider.java @@ -59,13 +59,17 @@ public class ConnectionLabelProvider extends LabelProvider { Connection conn = ( Connection ) obj; + boolean isConnected = conn.getConnectionWrapper().isConnected(); + boolean isSecured = conn.getConnectionWrapper().isSecured(); + String unsecuredWarning = isConnected && !isSecured ? " UNSECURED! " : ""; //$NON-NLS-1$ //$NON-NLS-2$ + if ( conn.getEncryptionMethod() == EncryptionMethod.LDAPS ) { - return conn.getName() + " (LDAPS)"; //$NON-NLS-1$ + return conn.getName() + unsecuredWarning + " (LDAPS)"; //$NON-NLS-1$ } else if ( conn.getEncryptionMethod() == EncryptionMethod.START_TLS ) { - return conn.getName() + " (StartTLS)"; //$NON-NLS-1$ + return conn.getName() + unsecuredWarning + " (StartTLS)"; //$NON-NLS-1$ } else { @@ -99,18 +103,21 @@ public class ConnectionLabelProvider extends LabelProvider { Connection conn = ( Connection ) obj; - if ( ( conn.getEncryptionMethod() == EncryptionMethod.LDAPS ) - || ( conn.getEncryptionMethod() == EncryptionMethod.START_TLS ) ) + boolean isConnected = conn.getConnectionWrapper().isConnected(); + boolean isSecured = conn.getConnectionWrapper().isSecured(); + boolean isEncryptionConfigured = conn.getEncryptionMethod().isEncrytped(); + + if ( isConnected ) { - return conn.getConnectionWrapper().isConnected() ? ConnectionUIPlugin.getDefault().getImage( + return isSecured ? ConnectionUIPlugin.getDefault().getImage( ConnectionUIConstants.IMG_CONNECTION_SSL_CONNECTED ) : ConnectionUIPlugin.getDefault().getImage( - ConnectionUIConstants.IMG_CONNECTION_SSL_DISCONNECTED ); + ConnectionUIConstants.IMG_CONNECTION_CONNECTED ); } else { - return conn.getConnectionWrapper().isConnected() ? ConnectionUIPlugin.getDefault().getImage( - ConnectionUIConstants.IMG_CONNECTION_CONNECTED ) + return isEncryptionConfigured ? ConnectionUIPlugin.getDefault().getImage( + ConnectionUIConstants.IMG_CONNECTION_SSL_DISCONNECTED ) : ConnectionUIPlugin.getDefault().getImage( ConnectionUIConstants.IMG_CONNECTION_DISCONNECTED ); } diff --git a/tests/test.integration.core/src/main/java/org/apache/directory/studio/test/integration/core/DirectoryApiConnectionWrapperTest.java b/tests/test.integration.core/src/main/java/org/apache/directory/studio/test/integration/core/DirectoryApiConnectionWrapperTest.java index 6ebf8fc..49000f2 100644 --- a/tests/test.integration.core/src/main/java/org/apache/directory/studio/test/integration/core/DirectoryApiConnectionWrapperTest.java +++ b/tests/test.integration.core/src/main/java/org/apache/directory/studio/test/integration/core/DirectoryApiConnectionWrapperTest.java @@ -44,6 +44,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.net.ConnectException; import java.nio.channels.UnresolvedAddressException; +import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -66,6 +67,8 @@ import org.apache.directory.api.ldap.extras.extended.pwdModify.PasswordModifyReq //import org.apache.directory.api.ldap.extras.extended.startTransaction.StartTransactionResponse; import org.apache.directory.api.ldap.extras.extended.whoAmI.WhoAmIRequest; import org.apache.directory.api.ldap.extras.extended.whoAmI.WhoAmIResponse; +import org.apache.directory.api.ldap.model.constants.SaslQoP; +import org.apache.directory.api.ldap.model.constants.SaslSecurityStrength; import org.apache.directory.api.ldap.model.entry.DefaultAttribute; import org.apache.directory.api.ldap.model.entry.DefaultEntry; import org.apache.directory.api.ldap.model.entry.DefaultModification; @@ -73,6 +76,7 @@ import org.apache.directory.api.ldap.model.entry.Entry; import org.apache.directory.api.ldap.model.entry.Modification; import org.apache.directory.api.ldap.model.entry.ModificationOperation; import org.apache.directory.api.ldap.model.exception.LdapAuthenticationException; +import org.apache.directory.api.ldap.model.exception.LdapAuthenticationNotSupportedException; import org.apache.directory.api.ldap.model.exception.LdapException; import org.apache.directory.api.ldap.model.exception.LdapLoopDetectedException; import org.apache.directory.api.ldap.model.message.ExtendedResponse; @@ -88,6 +92,7 @@ import org.apache.directory.studio.connection.core.ConnectionCorePlugin; import org.apache.directory.studio.connection.core.ConnectionParameter; import org.apache.directory.studio.connection.core.ConnectionParameter.AuthenticationMethod; import org.apache.directory.studio.connection.core.ConnectionParameter.EncryptionMethod; +import org.apache.directory.studio.connection.core.ICertificateHandler.TrustLevel; import org.apache.directory.studio.connection.core.IReferralHandler; import org.apache.directory.studio.connection.core.event.ConnectionEventRegistry; import org.apache.directory.studio.connection.core.io.ConnectionWrapper; @@ -99,9 +104,9 @@ import org.apache.directory.studio.ldapbrowser.core.jobs.InitializeRootDSERunnab import org.apache.directory.studio.ldapbrowser.core.model.impl.BrowserConnection; import org.apache.directory.studio.test.integration.junit5.LdapServerType; import org.apache.directory.studio.test.integration.junit5.LdapServersSource; +import org.apache.directory.studio.test.integration.junit5.LdapServersSource.Mode; import org.apache.directory.studio.test.integration.junit5.SkipTestIfLdapServerIsNotAvailableInterceptor; import org.apache.directory.studio.test.integration.junit5.TestLdapServer; -import org.apache.directory.studio.test.integration.junit5.LdapServersSource.Mode; import org.apache.mina.util.AvailablePortFinder; import org.eclipse.core.runtime.NullProgressMonitor; import org.junit.jupiter.api.AfterAll; @@ -128,6 +133,7 @@ public class DirectoryApiConnectionWrapperTest public static void suspendEventFiringInCurrentThread() { ConnectionEventRegistry.suspendEventFiringInCurrentThread(); + ConnectionCorePlugin.getDefault().setCertificateHandler( null ); } @@ -149,29 +155,76 @@ public class DirectoryApiConnectionWrapperTest /** - * Tests connecting to the server. + * Tests connecting to the server without encryption. */ @ParameterizedTest - @LdapServersSource - public void testConnect( TestLdapServer ldapServer ) + @LdapServersSource(mode = Mode.All) + public void testConnectPlain( TestLdapServer ldapServer ) { StudioProgressMonitor monitor = getProgressMonitor(); - ConnectionParameter connectionParameter = new ConnectionParameter( null, ldapServer.getHost(), - ldapServer.getPort(), EncryptionMethod.NONE, AuthenticationMethod.NONE, null, null, null, true, null, - 30000L ); - Connection connection = new Connection( connectionParameter ); - ConnectionWrapper connectionWrapper = connection.getConnectionWrapper(); + getConnection( monitor, ldapServer, null, null ); assertFalse( connectionWrapper.isConnected() ); - connectionWrapper.connect( monitor ); + X509Certificate[] certificates = connectionWrapper.connect( monitor ); assertTrue( connectionWrapper.isConnected() ); + assertFalse( connectionWrapper.isSecured() ); assertNull( monitor.getException() ); + assertNull( certificates ); connectionWrapper.disconnect(); assertFalse( connectionWrapper.isConnected() ); + } + - // TODO: SSL, StartTLS + /** + * Tests connecting to the server using ldaps:// encryption. + */ + @ParameterizedTest + @LdapServersSource(mode = Mode.All) + public void testConnectLdaps( TestLdapServer ldapServer ) + { + StudioProgressMonitor monitor = getProgressMonitor(); + Connection connection = getConnection( monitor, ldapServer, null, null ); + connection.setPort( ldapServer.getPortSSL() ); + connection.setEncryptionMethod( EncryptionMethod.LDAPS ); + acceptAllCertificates(); + + assertFalse( connectionWrapper.isConnected() ); + + X509Certificate[] certificates = connectionWrapper.connect( monitor ); + assertTrue( connectionWrapper.isConnected() ); + assertTrue( connectionWrapper.isSecured() ); + assertNull( monitor.getException() ); + assertNotNull( certificates ); + + connectionWrapper.disconnect(); + assertFalse( connectionWrapper.isConnected() ); + } + + + /** + * Tests connecting to the server using StartTLS encryption. + */ + @ParameterizedTest + @LdapServersSource(mode = Mode.All) + public void testConnectStartTls( TestLdapServer ldapServer ) + { + StudioProgressMonitor monitor = getProgressMonitor(); + Connection connection = getConnection( monitor, ldapServer, null, null ); + connection.setEncryptionMethod( EncryptionMethod.START_TLS ); + acceptAllCertificates(); + + assertFalse( connectionWrapper.isConnected() ); + + X509Certificate[] certificates = connectionWrapper.connect( monitor ); + assertTrue( connectionWrapper.isConnected() ); + assertTrue( connectionWrapper.isSecured() ); + assertNull( monitor.getException() ); + assertNotNull( certificates ); + + connectionWrapper.disconnect(); + assertFalse( connectionWrapper.isConnected() ); } @@ -179,21 +232,16 @@ public class DirectoryApiConnectionWrapperTest * Test failed connections to the server. */ @ParameterizedTest - @LdapServersSource + @LdapServersSource(mode = Mode.All) public void testConnectFailures( TestLdapServer ldapServer ) { StudioProgressMonitor monitor = null; - ConnectionParameter connectionParameter = null; Connection connection = null; - ConnectionWrapper connectionWrapper = null; // invalid port monitor = getProgressMonitor(); - connectionParameter = new ConnectionParameter( null, ldapServer.getHost(), - AvailablePortFinder.getNextAvailable(), EncryptionMethod.NONE, AuthenticationMethod.NONE, null, null, null, - true, null, 30000L ); - connection = new Connection( connectionParameter ); - connectionWrapper = connection.getConnectionWrapper(); + connection = getConnection( monitor, ldapServer, null, null ); + connection.setPort( AvailablePortFinder.getNextAvailable() ); connectionWrapper.connect( monitor ); assertFalse( connectionWrapper.isConnected() ); assertNotNull( monitor.getException() ); @@ -205,10 +253,8 @@ public class DirectoryApiConnectionWrapperTest // unknown host monitor = getProgressMonitor(); - connectionParameter = new ConnectionParameter( null, "555.555.555.555", ldapServer.getPort(), - EncryptionMethod.NONE, AuthenticationMethod.NONE, null, null, null, true, null, 30000L ); - connection = new Connection( connectionParameter ); - connectionWrapper = connection.getConnectionWrapper(); + connection = getConnection( monitor, ldapServer, null, null ); + connection.setHost( "555.555.555.555" ); connectionWrapper.connect( monitor ); assertFalse( connectionWrapper.isConnected() ); assertNotNull( monitor.getException() ); @@ -217,30 +263,222 @@ public class DirectoryApiConnectionWrapperTest assertTrue( monitor.getException().getCause() instanceof InvalidConnectionException ); assertNotNull( monitor.getException().getCause().getCause() ); assertTrue( monitor.getException().getCause().getCause() instanceof UnresolvedAddressException ); + } + + + /** + * Test binding to the server using simple auth and no encryption. + */ + @ParameterizedTest + @LdapServersSource(mode = Mode.All) + public void testSimpleBindPlain( TestLdapServer ldapServer ) + { + ldapServer.setConfidentialityRequired( false ); + StudioProgressMonitor monitor = getProgressMonitor(); + getConnection( monitor, ldapServer, ldapServer.getAdminDn(), ldapServer.getAdminPassword() ); + + assertFalse( connectionWrapper.isConnected() ); + + connectionWrapper.connect( monitor ); + connectionWrapper.bind( monitor ); + assertTrue( connectionWrapper.isConnected() ); + assertFalse( connectionWrapper.isSecured() ); + assertNull( monitor.getException() ); - // TODO: SSL, StartTLS + connectionWrapper.unbind(); + connectionWrapper.disconnect(); + assertFalse( connectionWrapper.isConnected() ); } /** - * Test binding to the server. + * Test binding to the server using simple auth and no encryption should fail if the server requires confidentially. */ @ParameterizedTest - @LdapServersSource - public void testBind( TestLdapServer ldapServer ) + @LdapServersSource(mode = Mode.All) + public void testSimpleBindPlainConfidentiallyRequired( TestLdapServer ldapServer ) { + ldapServer.setConfidentialityRequired( true ); StudioProgressMonitor monitor = getProgressMonitor(); - ConnectionParameter connectionParameter = new ConnectionParameter( null, ldapServer.getHost(), - ldapServer.getPort(), EncryptionMethod.NONE, AuthenticationMethod.SIMPLE, ldapServer.getAdminDn(), - ldapServer.getAdminPassword(), null, true, null, 30000L ); - Connection connection = new Connection( connectionParameter ); - ConnectionWrapper connectionWrapper = connection.getConnectionWrapper(); + getConnection( monitor, ldapServer, ldapServer.getAdminDn(), ldapServer.getAdminPassword() ); + + assertFalse( connectionWrapper.isConnected() ); + + connectionWrapper.connect( monitor ); + connectionWrapper.bind( monitor ); + + assertFalse( connectionWrapper.isConnected() ); + assertFalse( connectionWrapper.isSecured() ); + assertNotNull( monitor.getException() ); + assertTrue( monitor.getException() instanceof StudioLdapException ); + assertTrue( monitor.getException().getMessage().contains( "[LDAP result code 13 - confidentialityRequired]" ) ); + assertNotNull( monitor.getException().getCause() ); + assertTrue( monitor.getException().getCause() instanceof LdapAuthenticationNotSupportedException ); + } + + + /** + * Test binding to the server using simple auth and ldaps:// encryption. + */ + @ParameterizedTest + @LdapServersSource(mode = Mode.All) + public void testSimpleBindLdaps( TestLdapServer ldapServer ) + { + ldapServer.setConfidentialityRequired( true ); + StudioProgressMonitor monitor = getProgressMonitor(); + Connection connection = getConnection( monitor, ldapServer, ldapServer.getAdminDn(), + ldapServer.getAdminPassword() ); + connection.setPort( ldapServer.getPortSSL() ); + connection.setEncryptionMethod( EncryptionMethod.LDAPS ); + acceptAllCertificates(); + + assertFalse( connectionWrapper.isConnected() ); + + connectionWrapper.connect( monitor ); + connectionWrapper.bind( monitor ); + assertTrue( connectionWrapper.isConnected() ); + assertTrue( connectionWrapper.isSecured() ); + assertNull( monitor.getException() ); + + connectionWrapper.unbind(); + connectionWrapper.disconnect(); + assertFalse( connectionWrapper.isConnected() ); + } + + + /** + * Test binding to the server using simple auth and StartTLS encryption. + */ + @ParameterizedTest + @LdapServersSource(mode = Mode.All) + public void testSimpleBindStartTls( TestLdapServer ldapServer ) + { + ldapServer.setConfidentialityRequired( true ); + StudioProgressMonitor monitor = getProgressMonitor(); + Connection connection = getConnection( monitor, ldapServer, ldapServer.getAdminDn(), + ldapServer.getAdminPassword() ); + connection.setEncryptionMethod( EncryptionMethod.START_TLS ); + acceptAllCertificates(); + + assertFalse( connectionWrapper.isConnected() ); + + connectionWrapper.connect( monitor ); + connectionWrapper.bind( monitor ); + assertTrue( connectionWrapper.isConnected() ); + assertTrue( connectionWrapper.isSecured() ); + assertNull( monitor.getException() ); + + connectionWrapper.unbind(); + connectionWrapper.disconnect(); + assertFalse( connectionWrapper.isConnected() ); + } + + + /** + * Test binding to the server using SASL and no encryption. + */ + @ParameterizedTest + @LdapServersSource(mode = Mode.All, except = LdapServerType.Fedora389ds) + public void testSaslBindPlain( TestLdapServer ldapServer ) + { + ldapServer.setConfidentialityRequired( false ); + StudioProgressMonitor monitor = getProgressMonitor(); + Connection connection = getConnection( monitor, ldapServer, "user.1", "password" ); + connection.setAuthMethod( AuthenticationMethod.SASL_DIGEST_MD5 ); + + assertFalse( connectionWrapper.isConnected() ); + + connectionWrapper.connect( monitor ); + connectionWrapper.bind( monitor ); + + assertTrue( connectionWrapper.isConnected() ); + assertFalse( connectionWrapper.isSecured() ); + assertNull( monitor.getException() ); + + connectionWrapper.unbind(); + connectionWrapper.disconnect(); + assertFalse( connectionWrapper.isConnected() ); + } + + + /** + * Test binding to the server using SASL and no encryption should fail if the server requires confidentially. + */ + @ParameterizedTest + @LdapServersSource(mode = Mode.All, except = LdapServerType.Fedora389ds) + public void testSaslBindPlainConfidentiallyRequired( TestLdapServer ldapServer ) + { + ldapServer.setConfidentialityRequired( true ); + StudioProgressMonitor monitor = getProgressMonitor(); + Connection connection = getConnection( monitor, ldapServer, "user.1", "password" ); + connection.setAuthMethod( AuthenticationMethod.SASL_DIGEST_MD5 ); + + assertFalse( connectionWrapper.isConnected() ); + + connectionWrapper.connect( monitor ); + connectionWrapper.bind( monitor ); + + assertFalse( connectionWrapper.isConnected() ); + assertFalse( connectionWrapper.isSecured() ); + assertNotNull( monitor.getException() ); + assertTrue( monitor.getException() instanceof StudioLdapException ); + assertTrue( monitor.getException().getMessage().contains( "[LDAP result code 13 - confidentialityRequired]" ) ); + assertNotNull( monitor.getException().getCause() ); + assertTrue( monitor.getException().getCause() instanceof LdapAuthenticationNotSupportedException ); + } + + + /** + * Test binding to the server using SASL and ldaps:// encryption. + */ + @ParameterizedTest + @LdapServersSource(mode = Mode.All, except = LdapServerType.Fedora389ds) + public void testSaslBindLdaps( TestLdapServer ldapServer ) + { + ldapServer.setConfidentialityRequired( true ); + StudioProgressMonitor monitor = getProgressMonitor(); + Connection connection = getConnection( monitor, ldapServer, "user.1", "password" ); + connection.setPort( ldapServer.getPortSSL() ); + connection.setEncryptionMethod( EncryptionMethod.LDAPS ); + connection.setAuthMethod( AuthenticationMethod.SASL_DIGEST_MD5 ); + acceptAllCertificates(); + + assertFalse( connectionWrapper.isConnected() ); + + connectionWrapper.connect( monitor ); + connectionWrapper.bind( monitor ); + + assertTrue( connectionWrapper.isConnected() ); + assertTrue( connectionWrapper.isSecured() ); + assertNull( monitor.getException() ); + + connectionWrapper.unbind(); + connectionWrapper.disconnect(); + assertFalse( connectionWrapper.isConnected() ); + } + + + /** + * Test binding to the server using SASL and StartTLS encryption. + */ + @ParameterizedTest + @LdapServersSource(mode = Mode.All, except = LdapServerType.Fedora389ds) + public void testSaslBindStartTls( TestLdapServer ldapServer ) + { + ldapServer.setConfidentialityRequired( true ); + StudioProgressMonitor monitor = getProgressMonitor(); + Connection connection = getConnection( monitor, ldapServer, "user.1", "password" ); + connection.setEncryptionMethod( EncryptionMethod.START_TLS ); + connection.setAuthMethod( AuthenticationMethod.SASL_DIGEST_MD5 ); + acceptAllCertificates(); assertFalse( connectionWrapper.isConnected() ); connectionWrapper.connect( monitor ); connectionWrapper.bind( monitor ); + assertTrue( connectionWrapper.isConnected() ); + assertTrue( connectionWrapper.isSecured() ); assertNull( monitor.getException() ); connectionWrapper.unbind(); @@ -257,17 +495,10 @@ public class DirectoryApiConnectionWrapperTest public void testBindFailures( TestLdapServer ldapServer ) { StudioProgressMonitor monitor = null; - ConnectionParameter connectionParameter = null; - Connection connection = null; - ConnectionWrapper connectionWrapper = null; // simple auth with invalid user monitor = getProgressMonitor(); - connectionParameter = new ConnectionParameter( null, ldapServer.getHost(), ldapServer.getPort(), - EncryptionMethod.NONE, AuthenticationMethod.SIMPLE, "cn=invalid," + USERS_DN, "invalid", null, true, - null, 30000L ); - connection = new Connection( connectionParameter ); - connectionWrapper = connection.getConnectionWrapper(); + getConnection( monitor, ldapServer, "cn=invalid," + USERS_DN, "invalid" ); connectionWrapper.connect( monitor ); connectionWrapper.bind( monitor ); assertFalse( connectionWrapper.isConnected() ); @@ -283,11 +514,7 @@ public class DirectoryApiConnectionWrapperTest // simple auth with invalid password monitor = getProgressMonitor(); - connectionParameter = new ConnectionParameter( null, ldapServer.getHost(), ldapServer.getPort(), - EncryptionMethod.NONE, AuthenticationMethod.SIMPLE, ldapServer.getAdminDn(), "invalid", null, true, null, - 30000L ); - connection = new Connection( connectionParameter ); - connectionWrapper = connection.getConnectionWrapper(); + getConnection( monitor, ldapServer, ldapServer.getAdminDn(), "invalid" ); connectionWrapper.connect( monitor ); connectionWrapper.bind( monitor ); assertFalse( connectionWrapper.isConnected() ); @@ -1055,58 +1282,57 @@ public class DirectoryApiConnectionWrapperTest } - protected ConnectionWrapper getConnectionWrapper( StudioProgressMonitor monitor, TestLdapServer ldapServer, - String dn, String password ) + protected void acceptAllCertificates() { - // simple auth without principal and credential - ConnectionParameter connectionParameter = new ConnectionParameter( null, ldapServer.getHost(), - ldapServer.getPort(), EncryptionMethod.NONE, AuthenticationMethod.SIMPLE, dn, password, null, false, null, - 30000L ); + ConnectionCorePlugin.getDefault().setCertificateHandler( ( host, certChain, failCauses ) -> { + return TrustLevel.Permanent; + } ); + } - Connection connection = new Connection( connectionParameter ); - connectionWrapper = connection.getConnectionWrapper(); - connectionWrapper.connect( monitor ); - connectionWrapper.bind( monitor ); + protected ConnectionWrapper getConnectionWrapper( StudioProgressMonitor monitor, TestLdapServer ldapServer ) + { + return getConnectionWrapper( monitor, ldapServer, ldapServer.getAdminDn(), + ldapServer.getAdminPassword() ); + } - assertTrue( connectionWrapper.isConnected() ); - IReferralHandler referralHandler = referralUrls -> { - return connection; - }; - ConnectionCorePlugin.getDefault().setReferralHandler( referralHandler ); + protected ConnectionWrapper getConnectionWrapper( StudioProgressMonitor monitor, TestLdapServer ldapServer, + String dn, String password ) + { + getConnection( monitor, ldapServer, dn, password ); + connectionWrapper.connect( monitor ); + connectionWrapper.bind( monitor ); assertTrue( connectionWrapper.isConnected() ); assertNull( monitor.getException() ); return connectionWrapper; + } - protected ConnectionWrapper getConnectionWrapper( StudioProgressMonitor monitor, TestLdapServer ldapServer ) + protected Connection getConnection( StudioProgressMonitor monitor, TestLdapServer ldapServer, + String dn, String password ) { // simple auth without principal and credential ConnectionParameter connectionParameter = new ConnectionParameter( null, ldapServer.getHost(), - ldapServer.getPort(), EncryptionMethod.NONE, AuthenticationMethod.SIMPLE, ldapServer.getAdminDn(), - ldapServer.getAdminPassword(), null, false, null, 30000L ); + ldapServer.getPort(), EncryptionMethod.NONE, AuthenticationMethod.SIMPLE, dn, password, null, false, null, + 30000L ); + connectionParameter.setSaslQop( SaslQoP.AUTH_CONF ); + connectionParameter.setSaslSecurityStrength( SaslSecurityStrength.HIGH ); + connectionParameter.setSaslMutualAuthentication( true ); Connection connection = new Connection( connectionParameter ); - connectionWrapper = connection.getConnectionWrapper(); - connectionWrapper.connect( monitor ); - connectionWrapper.bind( monitor ); - - assertTrue( connectionWrapper.isConnected() ); - IReferralHandler referralHandler = referralUrls -> { return connection; }; ConnectionCorePlugin.getDefault().setReferralHandler( referralHandler ); - assertTrue( connectionWrapper.isConnected() ); - assertNull( monitor.getException() ); + connectionWrapper = connection.getConnectionWrapper(); - return connectionWrapper; + return connection; } } diff --git a/tests/test.integration.core/src/main/java/org/apache/directory/studio/test/integration/junit5/ApacheDirectoryServer.java b/tests/test.integration.core/src/main/java/org/apache/directory/studio/test/integration/junit5/ApacheDirectoryServer.java index ade8ddf..c93a945 100644 --- a/tests/test.integration.core/src/main/java/org/apache/directory/studio/test/integration/junit5/ApacheDirectoryServer.java +++ b/tests/test.integration.core/src/main/java/org/apache/directory/studio/test/integration/junit5/ApacheDirectoryServer.java @@ -86,7 +86,7 @@ public class ApacheDirectoryServer extends TestLdapServer service.setInterceptors( service.getInterceptors().stream() .filter( i -> !i.getName().equals( "ConfigurableHashingInterceptor" ) ) .collect( Collectors.toList() ) ); - System.out.println( service.getInterceptors() ); + service.setAllowAnonymousAccess( true ); server = new LdapServer(); server.setDirectoryService( service ); @@ -97,6 +97,7 @@ public class ApacheDirectoryServer extends TestLdapServer server.addTransports( ldaps ); server.addSaslMechanismHandler( "SIMPLE", new SimpleMechanismHandler() ); + server.addSaslMechanismHandler( "CRAM-MD5", new CramMd5MechanismHandler() ); server.addSaslMechanismHandler( "DIGEST-MD5", new DigestMd5MechanismHandler() ); server.setSaslRealms( Collections.singletonList( "EXAMPLE.ORG" ) ); server.setSaslHost( getHost() ); @@ -153,6 +154,13 @@ public class ApacheDirectoryServer extends TestLdapServer } + @Override + public void setConfidentialityRequired( boolean confidentialityRequired ) + { + server.setConfidentialityRequired( confidentialityRequired ); + } + + private ApacheDirectoryServer( int port, int portSSL ) { super( LdapServerType.ApacheDS, LOCALHOST, port, portSSL, "uid=admin,ou=system", "secret" ); diff --git a/tests/test.integration.core/src/main/java/org/apache/directory/studio/test/integration/junit5/Fedora389dsLdapServer.java b/tests/test.integration.core/src/main/java/org/apache/directory/studio/test/integration/junit5/Fedora389dsLdapServer.java index b856ee5..7f8a0ac 100644 --- a/tests/test.integration.core/src/main/java/org/apache/directory/studio/test/integration/junit5/Fedora389dsLdapServer.java +++ b/tests/test.integration.core/src/main/java/org/apache/directory/studio/test/integration/junit5/Fedora389dsLdapServer.java @@ -23,6 +23,10 @@ package org.apache.directory.studio.test.integration.junit5; import static org.apache.directory.studio.test.integration.junit5.Constants.LOCALHOST; +import org.apache.directory.api.ldap.model.entry.DefaultModification; +import org.apache.directory.api.ldap.model.entry.Modification; +import org.apache.directory.api.ldap.model.entry.ModificationOperation; + /** * An 389ds implementation of a test LDAP server. @@ -52,4 +56,15 @@ public class Fedora389dsLdapServer extends TestLdapServer FEDORA_389DS_ADMIN_DN, FEDORA_389DS_ADMIN_PASSWORD ); } + + @Override + public void setConfidentialityRequired( boolean confidentialityRequired ) + { + withAdminConnection( connection -> { + Modification modification = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, + "nsslapd-require-secure-binds", confidentialityRequired ? "on" : "off" ); + connection.modify( "cn=config", modification ); + } ); + + } } diff --git a/tests/test.integration.core/src/main/java/org/apache/directory/studio/test/integration/junit5/OpenLdapServer.java b/tests/test.integration.core/src/main/java/org/apache/directory/studio/test/integration/junit5/OpenLdapServer.java index 23c6495..69b80cb 100644 --- a/tests/test.integration.core/src/main/java/org/apache/directory/studio/test/integration/junit5/OpenLdapServer.java +++ b/tests/test.integration.core/src/main/java/org/apache/directory/studio/test/integration/junit5/OpenLdapServer.java @@ -23,10 +23,13 @@ package org.apache.directory.studio.test.integration.junit5; import static org.apache.directory.studio.test.integration.junit5.Constants.LOCALHOST; +import org.apache.directory.api.ldap.model.entry.DefaultModification; import org.apache.directory.api.ldap.model.entry.Modification; +import org.apache.directory.api.ldap.model.entry.ModificationOperation; +import org.apache.directory.api.ldap.model.exception.LdapNoSuchAttributeException; import org.apache.directory.api.ldap.model.ldif.LdifEntry; import org.apache.directory.api.ldap.model.ldif.LdifReader; -import org.apache.directory.ldap.client.api.LdapNetworkConnection; +import org.apache.directory.ldap.client.api.LdapConnection; /** @@ -63,7 +66,7 @@ public class OpenLdapServer extends TestLdapServer { super.prepare(); - try ( LdapNetworkConnection connection = new LdapNetworkConnection( OPENLDAP_HOST, OPENLDAP_PORT ); + try ( LdapConnection connection = openConnection(); LdifReader ldifReader = new LdifReader( TestFixture.class.getResourceAsStream( "OpenLdapConfig.ldif" ) ) ) { connection.bind( OPENLDAP_CONFIG_DN, OPENLDAP_CONFIG_PASSWORD ); @@ -81,4 +84,25 @@ public class OpenLdapServer extends TestLdapServer } } + + @Override + public void setConfidentialityRequired( boolean confidentialityRequired ) + { + try ( LdapConnection connection = openConnection() ) + { + connection.bind( OPENLDAP_CONFIG_DN, OPENLDAP_CONFIG_PASSWORD ); + Modification modification = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, + "olcSecurity", confidentialityRequired ? "ssf=256 tls=256" : "ssf=0 tls=0" ); + connection.modify( "cn=config", modification ); + } + catch ( LdapNoSuchAttributeException e ) + { + // ignore + } + catch ( Exception e ) + { + throw new RuntimeException( "Unexpected exception: " + e, e ); + } + } + } diff --git a/tests/test.integration.core/src/main/java/org/apache/directory/studio/test/integration/junit5/TestLdapServer.java b/tests/test.integration.core/src/main/java/org/apache/directory/studio/test/integration/junit5/TestLdapServer.java index 9caa587..f0fe345 100644 --- a/tests/test.integration.core/src/main/java/org/apache/directory/studio/test/integration/junit5/TestLdapServer.java +++ b/tests/test.integration.core/src/main/java/org/apache/directory/studio/test/integration/junit5/TestLdapServer.java @@ -26,7 +26,9 @@ import org.apache.directory.api.ldap.model.exception.LdapException; import org.apache.directory.api.ldap.model.ldif.LdifEntry; import org.apache.directory.api.ldap.model.ldif.LdifReader; import org.apache.directory.ldap.client.api.LdapConnection; +import org.apache.directory.ldap.client.api.LdapConnectionConfig; import org.apache.directory.ldap.client.api.LdapNetworkConnection; +import org.apache.directory.ldap.client.api.NoVerificationTrustManager; import org.apache.directory.ldap.client.api.exception.InvalidConnectionException; @@ -83,13 +85,25 @@ public abstract class TestLdapServer public LdapConnection openAdminConnection() throws LdapException { - LdapConnection connection = new LdapNetworkConnection( host, port ); - connection.connect(); + LdapConnection connection = openConnection(); connection.bind( adminDn, adminPassword ); return connection; } + public LdapConnection openConnection() throws LdapException + { + LdapConnectionConfig config = new LdapConnectionConfig(); + config.setLdapHost( host ); + config.setLdapPort( port ); + config.setUseTls( true ); + config.setTrustManagers( new NoVerificationTrustManager() ); + LdapConnection connection = new LdapNetworkConnection( config ); + connection.connect(); + return connection; + } + + public void withAdminConnection( LdapConnectionConsumer consumer ) { try ( LdapConnection connection = openAdminConnection() ) @@ -129,6 +143,7 @@ public abstract class TestLdapServer TestFixture.createContextEntry( this ); TestFixture.cleanup( this ); TestFixture.importData( this ); + setConfidentialityRequired( false ); String serverSpecificLdif = getType().name() + ".ldif"; if ( TestFixture.class.getResource( serverSpecificLdif ) != null ) @@ -200,6 +215,9 @@ public abstract class TestLdapServer } + public abstract void setConfidentialityRequired( boolean confidentialityRequired ); + + @Override public String toString() { diff --git a/tests/test.integration.core/src/main/resources/org/apache/directory/studio/test/integration/junit5/OpenLdapConfig.ldif b/tests/test.integration.core/src/main/resources/org/apache/directory/studio/test/integration/junit5/OpenLdapConfig.ldif index 9fe06a5..158317f 100644 --- a/tests/test.integration.core/src/main/resources/org/apache/directory/studio/test/integration/junit5/OpenLdapConfig.ldif +++ b/tests/test.integration.core/src/main/resources/org/apache/directory/studio/test/integration/junit5/OpenLdapConfig.ldif @@ -36,8 +36,9 @@ dn: cn=config changetype: modify replace: olcAuthzRegexp olcAuthzRegexp: uid=([^,]*),cn=digest-md5,cn=auth uid=$1,ou=users,dc=example,dc=org +olcAuthzRegexp: uid=([^,]*),cn=cram-md5,cn=auth uid=$1,ou=users,dc=example,dc=org - replace: olcSaslSecProps -olcSaslSecProps: noplain,noanonymous,minssf=128 +olcSaslSecProps: noplain,noanonymous,minssf=0 - diff --git a/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/AbstractTestBase.java b/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/AbstractTestBase.java index b59294b..817847f 100644 --- a/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/AbstractTestBase.java +++ b/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/AbstractTestBase.java @@ -23,6 +23,7 @@ package org.apache.directory.studio.test.integration.ui; import static org.apache.directory.studio.test.integration.junit5.TestFixture.CONTEXT_DN; import static org.apache.directory.studio.test.integration.junit5.TestFixture.REFERRALS_DN; +import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -30,6 +31,7 @@ import java.util.List; import org.apache.commons.lang3.ArrayUtils; import org.apache.directory.api.ldap.model.name.Dn; import org.apache.directory.api.ldap.model.name.Rdn; +import org.apache.directory.studio.connection.core.ConnectionCorePlugin; import org.apache.directory.studio.test.integration.junit5.SkipTestIfLdapServerIsNotAvailableInterceptor; import org.apache.directory.studio.test.integration.junit5.TestLdapServer; import org.apache.directory.studio.test.integration.ui.bots.ApacheDSServersViewBot; @@ -76,6 +78,20 @@ public class AbstractTestBase @AfterEach final void tearDownBase() throws Exception { + // clear custom trust stores + X509Certificate[] permanentCertificates = ConnectionCorePlugin.getDefault().getPermanentTrustStoreManager() + .getCertificates(); + for ( X509Certificate certificate : permanentCertificates ) + { + ConnectionCorePlugin.getDefault().getPermanentTrustStoreManager().removeCertificate( certificate ); + } + X509Certificate[] temporaryCertificates = ConnectionCorePlugin.getDefault().getSessionTrustStoreManager() + .getCertificates(); + for ( X509Certificate certificate : temporaryCertificates ) + { + ConnectionCorePlugin.getDefault().getSessionTrustStoreManager().removeCertificate( certificate ); + } + connectionsViewBot.deleteTestConnections(); serversViewBot.deleteTestServers(); Assertions.genericTearDownAssertions(); diff --git a/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/CertificateValidationTest.java b/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/CertificateValidationTest.java index a78b757..54b555c 100644 --- a/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/CertificateValidationTest.java +++ b/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/CertificateValidationTest.java @@ -107,20 +107,6 @@ public class CertificateValidationTest extends AbstractTestBase @AfterEach public void tearDown() throws Exception { - // delete custom trust stores - X509Certificate[] permanentCertificates = ConnectionCorePlugin.getDefault().getPermanentTrustStoreManager() - .getCertificates(); - for ( X509Certificate certificate : permanentCertificates ) - { - ConnectionCorePlugin.getDefault().getPermanentTrustStoreManager().removeCertificate( certificate ); - } - X509Certificate[] temporaryCertificates = ConnectionCorePlugin.getDefault().getSessionTrustStoreManager() - .getCertificates(); - for ( X509Certificate certificate : temporaryCertificates ) - { - ConnectionCorePlugin.getDefault().getSessionTrustStoreManager().removeCertificate( certificate ); - } - // delete custom Java key store settings System.clearProperty( "javax.net.ssl.trustStore" ); System.clearProperty( "javax.net.ssl.trustStorePassword" ); diff --git a/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/NewConnectionWizardTest.java b/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/NewConnectionWizardTest.java index 93df03f..2fcba85 100644 --- a/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/NewConnectionWizardTest.java +++ b/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/NewConnectionWizardTest.java @@ -21,7 +21,6 @@ package org.apache.directory.studio.test.integration.ui; -import static org.hamcrest.CoreMatchers.anyOf; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -40,11 +39,13 @@ import org.apache.directory.studio.connection.core.Connection; import org.apache.directory.studio.connection.core.ConnectionCorePlugin; import org.apache.directory.studio.connection.core.ConnectionManager; import org.apache.directory.studio.connection.core.ConnectionParameter.AuthenticationMethod; -import org.apache.directory.studio.test.integration.junit5.Constants; +import org.apache.directory.studio.connection.core.ConnectionParameter.EncryptionMethod; import org.apache.directory.studio.test.integration.junit5.LdapServerType; import org.apache.directory.studio.test.integration.junit5.LdapServersSource; import org.apache.directory.studio.test.integration.junit5.LdapServersSource.Mode; import org.apache.directory.studio.test.integration.junit5.TestLdapServer; +import org.apache.directory.studio.test.integration.ui.bots.CertificateTrustDialogBot; +import org.apache.directory.studio.test.integration.ui.bots.ErrorDialogBot; import org.apache.directory.studio.test.integration.ui.bots.NewConnectionWizardBot; import org.apache.mina.util.AvailablePortFinder; import org.junit.jupiter.api.BeforeEach; @@ -342,18 +343,57 @@ public class NewConnectionWizardTest extends AbstractTestBase } - /** - * Creates a new connection using the new connection wizard. - */ + @ParameterizedTest + @LdapServersSource(mode = Mode.All) + public void testCreateConnectionNoEncryptionNoAuthOK( TestLdapServer server ) + { + setConnectionParameters( server, EncryptionMethod.NONE ); + + wizardBot.selectNoAuthentication(); + + finishAndAssertConnection( server, EncryptionMethod.NONE, AuthenticationMethod.NONE, "", "" ); + } + + @ParameterizedTest @LdapServersSource - public void testCreateConnection( TestLdapServer server ) + public void testCreateConnectionNoEncryptionNoAuthInvalidHostname( TestLdapServer server ) + { + setConnectionParameters( server, EncryptionMethod.NONE ); + + wizardBot.clickBackButton(); + String hostname = getInvalidHostName(); + wizardBot.typeHost( hostname ); + wizardBot.clickNextButton(); + wizardBot.selectNoAuthentication(); + + finishAndAssertConnectionError( hostname ); + } + + + @ParameterizedTest + @LdapServersSource(mode = Mode.All) + public void testCreateConnectionNoEncryptionSimpleAuthOK( TestLdapServer server ) throws UnknownHostException { // enter connection parameter wizardBot.typeConnectionName( getConnectionName() ); wizardBot.typeHost( server.getHost() ); wizardBot.typePort( server.getPort() ); + // click "Check Network Parameter" button + String result = wizardBot.clickCheckNetworkParameterButton(); + assertNull( result, "Expected OK" ); + + // enter IPv4 address as host + wizardBot.typeHost( InetAddress.getByName( server.getHost() ).getHostAddress() ); + + // click "Check Network Parameter" button + result = wizardBot.clickCheckNetworkParameterButton(); + assertNull( result, "Expected OK" ); + + // enter hostname as host again + wizardBot.typeHost( server.getHost() ); + // jump to auth page wizardBot.clickNextButton(); @@ -361,261 +401,470 @@ public class NewConnectionWizardTest extends AbstractTestBase wizardBot.typeUser( server.getAdminDn() ); wizardBot.typePassword( server.getAdminPassword() ); - // finish dialog - wizardBot.clickFinishButton( true ); - connectionsViewBot.waitForConnection( getConnectionName() ); - - // ensure connection was created - ConnectionManager connectionManager = ConnectionCorePlugin.getDefault().getConnectionManager(); - assertNotNull( connectionManager.getConnections() ); - assertEquals( 1, connectionManager.getConnections().length ); - Connection connection = connectionManager.getConnections()[0]; - assertEquals( getConnectionName(), connection.getName() ); - assertEquals( server.getHost(), connection.getHost() ); - assertEquals( server.getPort(), connection.getPort() ); - assertEquals( AuthenticationMethod.SIMPLE, connection.getAuthMethod() ); - assertEquals( server.getAdminDn(), connection.getBindPrincipal() ); - assertEquals( server.getAdminPassword(), connection.getBindPassword() ); - - // ensure connection is visible in Connections view - assertEquals( 1, connectionsViewBot.getCount() ); + // click "Check Network Parameter" button + result = wizardBot.clickCheckAuthenticationButton(); + assertNull( result, "Expected OK" ); - // close connection - connectionsViewBot.closeSelectedConnections(); + finishAndAssertConnection( server, EncryptionMethod.NONE, AuthenticationMethod.SIMPLE, + server.getAdminDn(), server.getAdminPassword() ); } - /** - * Tests the "Check Network Parameter" button. - */ @ParameterizedTest - @LdapServersSource - public void testCheckNetworkParameterButtonOK( TestLdapServer server ) throws UnknownHostException + @LdapServersSource(mode = Mode.All) + public void testCreateConnectionNoEncryptionSimpleAuthConfidentialityRequired( TestLdapServer server ) + { - // enter connection parameter with host name - wizardBot.typeConnectionName( getConnectionName() ); - wizardBot.typeHost( server.getHost() ); - wizardBot.typePort( server.getPort() ); + setConnectionParameters( server, EncryptionMethod.NONE ); - // click "Check Network Parameter" button - String result1 = wizardBot.clickCheckNetworkParameterButton(); - assertNull( result1, "Expected OK" ); + wizardBot.selectSimpleAuthentication(); + wizardBot.typeUser( server.getAdminDn() ); + wizardBot.typePassword( server.getAdminPassword() ); - // enter connection parameter with IPv4 address - wizardBot.typeHost( InetAddress.getByName( server.getHost() ).getHostAddress() ); - wizardBot.typePort( server.getPort() ); + server.setConfidentialityRequired( true ); - // click "Check Network Parameter" button - String result2 = wizardBot.clickCheckNetworkParameterButton(); - assertNull( result2, "Expected OK" ); - - // - // Don't know why this doesn't work with SWTBot. - // When testing manually it works. - // - // // enter connection parameter with IPv6 address - // wizardBot.typeHost( "[::1]" ); - // wizardBot.typePort( ldapService.getPort() ); - // - // // click "Check Network Parameter" button - // String result3 = wizardBot.clickCheckNetworkParameterButton(); - // assertNull( "Expected OK", result3 ); + String result = wizardBot.clickCheckAuthenticationButton(); + assertThat( result, containsString( "[LDAP result code 13 - confidentialityRequired]" ) ); + + finishAndAssertConnectionError( "[LDAP result code 13 - confidentialityRequired]" ); - wizardBot.clickCancelButton(); } - /** - * Tests the "Check Network Parameter" button. - */ - @Test - public void testCheckNetworkParameterButtonNotOK() + @ParameterizedTest + @LdapServersSource(mode = Mode.All, except = LdapServerType.Fedora389ds, reason = "SASL not configured for 389ds") + public void testCreateConnectionNoEncryptionSaslCramMd5OK( TestLdapServer server ) { - // enter connection parameter with invalid port - wizardBot.typeConnectionName( getConnectionName() ); - wizardBot.typeHost( Constants.LOCALHOST ); - wizardBot.typePort( AvailablePortFinder.getNextAvailable( 1024 ) ); - - // click "Check Network Parameter" button and get the result - String result1 = wizardBot.clickCheckNetworkParameterButton(); - assertNotNull( result1 ); - // LDAP API: Connection refused - // JNDI: The connection failed - assertThat( result1, - anyOf( containsString( "Connection refused" ), containsString( "The connection failed" ) ) ); - - // enter connection parameter with invalid host name - String hostname = "qwertzuiop.asdfghjkl.yxcvbnm"; - wizardBot.typeHost( hostname ); - wizardBot.typePort( 389 ); + setConnectionParameters( server, EncryptionMethod.NONE ); - // click "Check Network Parameter" button and get the result - String result2 = wizardBot.clickCheckNetworkParameterButton(); - assertNotNull( "Expected Error", result2 ); - // LDAP API: could not be resolved - // JNDI: The connection failed - assertThat( result2, - anyOf( containsString( "could not be resolved" ), containsString( "The connection failed" ) ) ); - assertThat( "Unknown host name must occur in error message", result2, containsString( hostname ) ); - - // disabled this test because it does not work properly - // as it depends from the network connection settings. - // // enter connection parameter with non-routed IP address - // String ipAddress = "10.11.12.13"; - // wizardBot.typeHost( ipAddress ); - // wizardBot.typePort( ldapServer.getPort() ); - // - // // click "Check Network Parameter" button and get the result - // String result3 = wizardBot.clickCheckNetworkParameterButton(); - // assertNotNull( "Expected Error", result3 ); - // assertTrue( "'No route to host' or 'Network is unreachable' message must occur in error message", // - // result3.contains( "No route to host" ) || result3.contains( "Network is unreachable" ) ); - // assertTrue( "IP address must occur in error message", result3.contains( ipAddress ) ); + wizardBot.selectCramMD5Authentication(); + wizardBot.typeUser( "user.1" ); + wizardBot.typePassword( "password" ); + wizardBot.selectQualityOfProtection( SaslQoP.AUTH ); + wizardBot.selectProtectionStrength( SaslSecurityStrength.HIGH ); - wizardBot.clickCancelButton(); + String result = wizardBot.clickCheckAuthenticationButton(); + assertNull( result, "Expected OK" ); + + finishAndAssertConnection( server, EncryptionMethod.NONE, AuthenticationMethod.SASL_CRAM_MD5, + "user.1", "password" ); } - /** - * Tests the "Check Authentication" button. - */ @ParameterizedTest - @LdapServersSource - public void testCheckAuthenticationButtonSimpleAuthOK( TestLdapServer server ) + @LdapServersSource(mode = Mode.All, except = LdapServerType.Fedora389ds, reason = "SASL not configured for 389ds") + public void testCreateConnectionNoEncryptionSaslDigestMd5OK( TestLdapServer server ) { - // enter connection parameter - wizardBot.typeConnectionName( getConnectionName() ); - wizardBot.typeHost( server.getHost() ); - wizardBot.typePort( server.getPort() ); - wizardBot.clickNextButton(); + setConnectionParameters( server, EncryptionMethod.NONE ); - // enter correct authentication parameter - wizardBot.typeUser( server.getAdminDn() ); - wizardBot.typePassword( server.getAdminPassword() ); + wizardBot.selectDigestMD5Authentication(); + wizardBot.typeUser( "user.1" ); + wizardBot.typePassword( "password" ); + if ( server.getType() == LdapServerType.ApacheDS ) + { + wizardBot.typeRealm( "EXAMPLE.ORG" ); + } + wizardBot.selectQualityOfProtection( SaslQoP.AUTH ); + wizardBot.selectProtectionStrength( SaslSecurityStrength.HIGH ); - // click "Check Network Parameter" button String result = wizardBot.clickCheckAuthenticationButton(); assertNull( result, "Expected OK" ); - wizardBot.clickCancelButton(); + finishAndAssertConnection( server, EncryptionMethod.NONE, AuthenticationMethod.SASL_DIGEST_MD5, + "user.1", "password" ); } - /** - * Tests the "Check Authentication" button. - */ @ParameterizedTest - @LdapServersSource - public void testCheckAuthenticationButtonSimpleAuthNotOK( TestLdapServer server ) + @LdapServersSource(mode = Mode.All, except = LdapServerType.Fedora389ds, reason = "SASL not configured for 389ds") + public void testCreateConnectionNoEncryptionSaslDigestMd5ConfidentialityRequired( TestLdapServer server ) { - // enter connection parameter - wizardBot.typeConnectionName( getConnectionName() ); - wizardBot.typeHost( server.getHost() ); - wizardBot.typePort( server.getPort() ); - wizardBot.clickNextButton(); + setConnectionParameters( server, EncryptionMethod.NONE ); + + wizardBot.selectDigestMD5Authentication(); + wizardBot.typeUser( "user.1" ); + wizardBot.typePassword( "password" ); + wizardBot.selectQualityOfProtection( SaslQoP.AUTH ); + wizardBot.selectProtectionStrength( SaslSecurityStrength.HIGH ); + + server.setConfidentialityRequired( true ); + + finishAndAssertConnectionError( "[LDAP result code 13 - confidentialityRequired]" ); + } + + + @ParameterizedTest + @LdapServersSource(mode = Mode.All) + public void testCreateConnectionLdapsEncryptionNoAuthOK( TestLdapServer server ) + { + setConnectionParameters( server, EncryptionMethod.LDAPS ); + + wizardBot.selectNoAuthentication(); + + finishAndAssertConnection( server, EncryptionMethod.LDAPS, AuthenticationMethod.NONE, "", "" ); + } + + + @ParameterizedTest + @LdapServersSource(mode = Mode.All) + public void testCreateConnectionLdapsEncryptionSimpleAuthOK( TestLdapServer server ) + { + setConnectionParameters( server, EncryptionMethod.LDAPS ); - // enter incorrect authentication parameter wizardBot.typeUser( server.getAdminDn() ); - wizardBot.typePassword( "secret45" ); + wizardBot.typePassword( server.getAdminPassword() ); + + finishAndAssertConnection( server, EncryptionMethod.LDAPS, AuthenticationMethod.SIMPLE, + server.getAdminDn(), server.getAdminPassword() ); + } + + + @ParameterizedTest + @LdapServersSource(mode = Mode.All) + public void testCreateConnectionLdapsEncryptionSimpleAuthInvalidCredentials( TestLdapServer server ) + { + setConnectionParameters( server, EncryptionMethod.LDAPS ); + + wizardBot.selectSimpleAuthentication(); + wizardBot.typeUser( "cn=invalid" ); + wizardBot.typePassword( "invalid" ); - // click "Check Network Parameter" button String result = wizardBot.clickCheckAuthenticationButton(); - assertNotNull( result ); assertThat( result, containsString( "[LDAP result code 49 - invalidCredentials]" ) ); - wizardBot.clickCancelButton(); + finishAndAssertConnectionError( "[LDAP result code 49 - invalidCredentials]" ); } - /** - * Tests the "Check Authentication" button. - */ @ParameterizedTest - @LdapServersSource(mode = Mode.All, except = LdapServerType.Fedora389ds) - public void testCheckAuthenticationButtonDigestMD5OK( TestLdapServer server ) + @LdapServersSource(mode = Mode.All, except = LdapServerType.Fedora389ds, reason = "SASL not configured for 389ds") + public void testCreateConnectionLdapsEncryptionSaslDigestMd5Ok( TestLdapServer server ) { - // enter connection parameter - wizardBot.typeConnectionName( getConnectionName() ); - wizardBot.typeHost( server.getHost() ); - wizardBot.typePort( server.getPort() ); - wizardBot.clickNextButton(); + setConnectionParameters( server, EncryptionMethod.LDAPS ); - // enter correct authentication parameter wizardBot.selectDigestMD5Authentication(); - wizardBot.typeUser( "user.8" ); + wizardBot.typeUser( "user.1" ); wizardBot.typePassword( "password" ); if ( server.getType() == LdapServerType.ApacheDS ) { wizardBot.typeRealm( "EXAMPLE.ORG" ); } - wizardBot.selectQualityOfProtection( SaslQoP.AUTH_CONF ); + wizardBot.selectQualityOfProtection( SaslQoP.AUTH ); + wizardBot.selectProtectionStrength( SaslSecurityStrength.HIGH ); + + finishAndAssertConnection( server, EncryptionMethod.LDAPS, AuthenticationMethod.SASL_DIGEST_MD5, + "user.1", "password" ); + } + + + @ParameterizedTest + @LdapServersSource(mode = Mode.All, except = LdapServerType.Fedora389ds, reason = "SASL not configured for 389ds") + public void testCreateConnectionLdapsEncryptionSaslDigestMd5InvalidCredentials( TestLdapServer server ) + { + setConnectionParameters( server, EncryptionMethod.LDAPS ); + + wizardBot.selectDigestMD5Authentication(); + wizardBot.typeUser( "user.1" ); + wizardBot.typePassword( "invalid" ); + if ( server.getType() == LdapServerType.ApacheDS ) + { + wizardBot.typeRealm( "EXAMPLE.ORG" ); + } + wizardBot.selectQualityOfProtection( SaslQoP.AUTH ); wizardBot.selectProtectionStrength( SaslSecurityStrength.HIGH ); - // click "Check Network Parameter" button String result = wizardBot.clickCheckAuthenticationButton(); - assertNull( result, "Expected OK" ); + assertThat( result, containsString( "[LDAP result code 49 - invalidCredentials]" ) ); - wizardBot.clickCancelButton(); + finishAndAssertConnectionError( "[LDAP result code 49 - invalidCredentials]" ); } - /** - * Tests the "Check Authentication" button. - */ @ParameterizedTest - @LdapServersSource(only = LdapServerType.OpenLdap) - public void testCheckAuthenticationButtonDigestMD5OKTooWeek( TestLdapServer server ) + @LdapServersSource(mode = Mode.All, except = LdapServerType.Fedora389ds, reason = "SASL not configured for 389ds") + public void testCreateConnectionLdapsEncryptionSaslDigestMd5InvalidRealm( TestLdapServer server ) { - // enter connection parameter - wizardBot.typeConnectionName( getConnectionName() ); - wizardBot.typeHost( server.getHost() ); - wizardBot.typePort( server.getPort() ); - wizardBot.clickNextButton(); + setConnectionParameters( server, EncryptionMethod.LDAPS ); - // enter correct authentication parameter wizardBot.selectDigestMD5Authentication(); - wizardBot.typeUser( "user.8" ); + wizardBot.typeUser( "user.1" ); wizardBot.typePassword( "password" ); + wizardBot.typeRealm( "APACHE.ORG" ); wizardBot.selectQualityOfProtection( SaslQoP.AUTH ); - wizardBot.selectProtectionStrength( SaslSecurityStrength.LOW ); + wizardBot.selectProtectionStrength( SaslSecurityStrength.HIGH ); - // click "Check Network Parameter" button String result = wizardBot.clickCheckAuthenticationButton(); - assertThat( result, containsString( "DIGEST-MD5: No common protection layer between client and server" ) ); + assertThat( result, containsString( "[LDAP result code" ) ); - wizardBot.clickCancelButton(); + finishAndAssertConnectionError( "[LDAP result code" ); } - /** - * Tests the "Check Authentication" button. - */ @ParameterizedTest - @LdapServersSource(mode = Mode.All, except = LdapServerType.Fedora389ds) - public void testCheckAuthenticationButtonDigestMD5NotOKWrongPassword( TestLdapServer server ) + @LdapServersSource(mode = Mode.All) + public void testCreateConnectionStartTlsEncryptionNoAuthOK( TestLdapServer server ) { - // enter connection parameter - wizardBot.typeConnectionName( getConnectionName() ); - wizardBot.typeHost( server.getHost() ); - wizardBot.typePort( server.getPort() ); - wizardBot.clickNextButton(); + setConnectionParameters( server, EncryptionMethod.START_TLS ); + + wizardBot.selectNoAuthentication(); + + finishAndAssertConnection( server, EncryptionMethod.START_TLS, AuthenticationMethod.NONE, "", "" ); + } + + + @ParameterizedTest + @LdapServersSource(mode = Mode.All) + public void testCreateConnectionStartTlsEncryptionSimpleAuthOK( TestLdapServer server ) + { + setConnectionParameters( server, EncryptionMethod.START_TLS ); + + wizardBot.typeUser( server.getAdminDn() ); + wizardBot.typePassword( server.getAdminPassword() ); + + finishAndAssertConnection( server, EncryptionMethod.START_TLS, AuthenticationMethod.SIMPLE, + server.getAdminDn(), server.getAdminPassword() ); + } + + + @ParameterizedTest + @LdapServersSource(mode = Mode.All) + public void testCreateConnectionStartTlsEncryptionSimpleAuthInvalidCredentials( TestLdapServer server ) + { + setConnectionParameters( server, EncryptionMethod.START_TLS ); + + wizardBot.selectSimpleAuthentication(); + wizardBot.typeUser( "cn=invalid" ); + wizardBot.typePassword( "invalid" ); + + String result = wizardBot.clickCheckAuthenticationButton(); + assertThat( result, containsString( "[LDAP result code 49 - invalidCredentials]" ) ); + + finishAndAssertConnectionError( "[LDAP result code 49 - invalidCredentials]" ); + } + + + @ParameterizedTest + @LdapServersSource(mode = Mode.All, except = LdapServerType.Fedora389ds, reason = "SASL not configured for 389ds") + public void testCreateConnectionStartTlsEncryptionSaslDigestMd5OK( TestLdapServer server ) + { + setConnectionParameters( server, EncryptionMethod.START_TLS ); - // enter correct authentication parameter wizardBot.selectDigestMD5Authentication(); - wizardBot.typeUser( "user.8" ); - wizardBot.typePassword( "wrong" ); + wizardBot.typeUser( "user.1" ); + wizardBot.typePassword( "password" ); if ( server.getType() == LdapServerType.ApacheDS ) { wizardBot.typeRealm( "EXAMPLE.ORG" ); } - wizardBot.selectQualityOfProtection( SaslQoP.AUTH_CONF ); + wizardBot.selectQualityOfProtection( SaslQoP.AUTH ); + wizardBot.selectProtectionStrength( SaslSecurityStrength.HIGH ); + + finishAndAssertConnection( server, EncryptionMethod.START_TLS, AuthenticationMethod.SASL_DIGEST_MD5, + "user.1", "password" ); + } + + + @ParameterizedTest + @LdapServersSource(mode = Mode.All, except = LdapServerType.Fedora389ds, reason = "SASL not configured for 389ds") + public void testCreateConnectionStartTlsEncryptionSaslDigestMd5InvalidCredentials( TestLdapServer server ) + { + setConnectionParameters( server, EncryptionMethod.START_TLS ); + + wizardBot.selectDigestMD5Authentication(); + wizardBot.typeUser( "user.1" ); + wizardBot.typePassword( "invalid" ); + if ( server.getType() == LdapServerType.ApacheDS ) + { + wizardBot.typeRealm( "EXAMPLE.ORG" ); + } + wizardBot.selectQualityOfProtection( SaslQoP.AUTH ); wizardBot.selectProtectionStrength( SaslSecurityStrength.HIGH ); - // click "Check Network Parameter" button String result = wizardBot.clickCheckAuthenticationButton(); assertThat( result, containsString( "[LDAP result code 49 - invalidCredentials]" ) ); + finishAndAssertConnectionError( "[LDAP result code 49 - invalidCredentials]" ); + } + + + @ParameterizedTest + @LdapServersSource(mode = Mode.All) + public void testCheckNetworkParameterButtonNoEncryptionNotOk( TestLdapServer server ) + { + setConnectionParameters( server, EncryptionMethod.NONE ); + wizardBot.clickBackButton(); + + // Invalid port + wizardBot.typePort( getInvalidPort() ); + String result = wizardBot.clickCheckNetworkParameterButton(); + assertThat( result, containsString( "The connection failed" ) ); + + // Invalid host + String hostname = getInvalidHostName(); + wizardBot.typeHost( hostname ); + result = wizardBot.clickCheckNetworkParameterButton(); + assertThat( result, containsString( "The connection failed" ) ); + assertThat( "Unknown host name must occur in error message", result, containsString( hostname ) ); + + wizardBot.clickCancelButton(); + } + + + @ParameterizedTest + @LdapServersSource(mode = Mode.All) + public void testCheckNetworkParameterButtonLdapsEncryptionNotOk( TestLdapServer server ) + { + setConnectionParameters( server, EncryptionMethod.LDAPS ); + wizardBot.clickBackButton(); + + // Invalid port + wizardBot.typePort( getInvalidPort() ); + String result = wizardBot.clickCheckNetworkParameterButton(); + assertThat( result, containsString( "The connection failed" ) ); + + // Non ldaps port + wizardBot.typePort( server.getPort() ); + result = wizardBot.clickCheckNetworkParameterButton(); + assertThat( result, containsString( "The connection failed" ) ); + + // Invalid host + String hostname = getInvalidHostName(); + wizardBot.typeHost( hostname ); + result = wizardBot.clickCheckNetworkParameterButton(); + assertThat( result, containsString( "The connection failed" ) ); + assertThat( "Unknown host name must occur in error message", result, containsString( hostname ) ); + + wizardBot.clickCancelButton(); + } + + + @ParameterizedTest + @LdapServersSource(mode = Mode.All) + public void testCheckNetworkParameterButtonStartTlsEncryptionNotOk( TestLdapServer server ) + { + setConnectionParameters( server, EncryptionMethod.START_TLS ); + wizardBot.clickBackButton(); + + // Invalid port + wizardBot.typePort( getInvalidPort() ); + String result = wizardBot.clickCheckNetworkParameterButton(); + assertThat( result, containsString( "The connection failed" ) ); + + // Ldaps port + wizardBot.typePort( server.getPortSSL() ); + result = wizardBot.clickCheckNetworkParameterButton(); + assertThat( result, containsString( "The connection failed" ) ); + + // Invalid host + String hostname = getInvalidHostName(); + wizardBot.typeHost( hostname ); + result = wizardBot.clickCheckNetworkParameterButton(); + assertThat( result, containsString( "The connection failed" ) ); + assertThat( "Unknown host name must occur in error message", result, containsString( hostname ) ); + wizardBot.clickCancelButton(); } + + private void setConnectionParameters( TestLdapServer server, EncryptionMethod encryptionMethod ) + { + wizardBot.typeConnectionName( getConnectionName() ); + wizardBot.typeHost( server.getHost() ); + wizardBot.typePort( encryptionMethod == EncryptionMethod.LDAPS ? server.getPortSSL() : server.getPort() ); + + if ( encryptionMethod == EncryptionMethod.LDAPS ) + { + wizardBot.selectLdapsEncryption(); + } + if ( encryptionMethod == EncryptionMethod.START_TLS ) + { + wizardBot.selectStartTlsEncryption(); + } + + if ( encryptionMethod != EncryptionMethod.NONE ) + { + server.setConfidentialityRequired( true ); + CertificateTrustDialogBot trustDialog = wizardBot + .clickCheckNetworkParameterButtonExpectingCertificateTrustDialog(); + trustDialog.selectTrustPermanent(); + trustDialog.clickOkButton(); + bot.button( "OK" ).click(); + } + + wizardBot.clickNextButton(); + } + + + private void finishAndAssertConnection( TestLdapServer server, EncryptionMethod encryptionMethod, + AuthenticationMethod authenticationMethod, String user, String password ) + { + wizardBot.clickFinishButton( true ); + + connectionsViewBot.waitForConnection( getConnectionName() ); + + // ensure connection was created + ConnectionManager connectionManager = ConnectionCorePlugin.getDefault().getConnectionManager(); + assertNotNull( connectionManager.getConnections() ); + assertEquals( 1, connectionManager.getConnections().length ); + Connection connection = connectionManager.getConnections()[0]; + assertEquals( getConnectionName(), connection.getName() ); + assertEquals( server.getHost(), connection.getHost() ); + assertEquals( encryptionMethod == EncryptionMethod.LDAPS ? server.getPortSSL() : server.getPort(), + connection.getPort() ); + assertEquals( user, connection.getBindPrincipal() ); + assertEquals( password, connection.getBindPassword() ); + assertEquals( authenticationMethod, connection.getAuthMethod() ); + assertTrue( connection.getConnectionWrapper().isConnected() ); + if ( encryptionMethod == EncryptionMethod.NONE ) + { + assertFalse( connection.getConnectionWrapper().isSecured() ); + } + else + { + assertTrue( connection.getConnectionWrapper().isSecured() ); + } + + // ensure connection is visible in Connections view + assertEquals( 1, connectionsViewBot.getCount() ); + + // close connection + connectionsViewBot.closeSelectedConnections(); + } + + + private void finishAndAssertConnectionError( String errorText ) + { + ErrorDialogBot errorBot = wizardBot.clickFinishButtonExpectingError(); + + String errorMessage = errorBot.getErrorMessage(); + String errorDetails = errorBot.getErrorDetails(); + assertNotNull( errorMessage ); + assertNotNull( errorDetails ); + assertThat( errorDetails, containsString( errorText ) ); + errorBot.clickOkButton(); + + ConnectionManager connectionManager = ConnectionCorePlugin.getDefault().getConnectionManager(); + assertNotNull( connectionManager.getConnections() ); + assertEquals( 1, connectionManager.getConnections().length ); + Connection connection = connectionManager.getConnections()[0]; + assertEquals( getConnectionName(), connection.getName() ); + assertFalse( connection.getConnectionWrapper().isConnected() ); + assertFalse( connection.getConnectionWrapper().isSecured() ); + + // ensure connection is visible in Connections view + assertEquals( 1, connectionsViewBot.getCount() ); + } + + + private int getInvalidPort() + { + return AvailablePortFinder.getNextAvailable( 1025 ); + } + + + private static String getInvalidHostName() + { + return "qwertzuiop.asdfghjkl.yxcvbnm"; + } + } diff --git a/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/bots/WizardBot.java b/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/bots/WizardBot.java index 28b8a5f..332bd91 100644 --- a/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/bots/WizardBot.java +++ b/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/bots/WizardBot.java @@ -94,6 +94,13 @@ public abstract class WizardBot extends DialogBot } + public ErrorDialogBot clickFinishButtonExpectingError() + { + String shellText = BotUtils.shell( () -> clickFinishButton(), "Error", "Problem Occurred" ).getText(); + return new ErrorDialogBot( shellText ); + } + + public boolean existsCategory( String category ) { TreeBot treeBot = new TreeBot( bot.tree() );