This is an automated email from the ASF dual-hosted git repository. quantranhong1999 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/james-project.git
commit 657749132f5a2034ee2daa402cca83af5eb47cce Author: Quan Tran <[email protected]> AuthorDate: Thu Jul 9 15:14:16 2026 +0700 JAMES-4210 Add SMTP explicit LOGIN SASL integration test Add a Guice-backed SMTP integration test proving auth.saslMechanisms can explicitly configure LoginSaslMechanismFactory and authenticate with AUTH LOGIN. Extend the test SMTP configuration template to render auth.requireSSL and explicit SASL mechanism entries. --- server/mailet/integration-testing/pom.xml | 5 + .../mailets/configuration/SmtpConfiguration.java | 25 +++++ .../src/main/resources/smtpserver.xml | 8 +- .../mailets/SmtpSaslMechanismIntegrationTest.java | 113 +++++++++++++++++++++ 4 files changed, 150 insertions(+), 1 deletion(-) diff --git a/server/mailet/integration-testing/pom.xml b/server/mailet/integration-testing/pom.xml index 2bed7540fc..0e051cc0b4 100644 --- a/server/mailet/integration-testing/pom.xml +++ b/server/mailet/integration-testing/pom.xml @@ -119,6 +119,11 @@ <groupId>com.rabbitmq</groupId> <artifactId>amqp-client</artifactId> </dependency> + <dependency> + <groupId>commons-net</groupId> + <artifactId>commons-net</artifactId> + <scope>test</scope> + </dependency> <dependency> <groupId>io.rest-assured</groupId> <artifactId>rest-assured</artifactId> diff --git a/server/mailet/integration-testing/src/main/java/org/apache/james/mailets/configuration/SmtpConfiguration.java b/server/mailet/integration-testing/src/main/java/org/apache/james/mailets/configuration/SmtpConfiguration.java index 3640af9d2a..66f8e82bf0 100644 --- a/server/mailet/integration-testing/src/main/java/org/apache/james/mailets/configuration/SmtpConfiguration.java +++ b/server/mailet/integration-testing/src/main/java/org/apache/james/mailets/configuration/SmtpConfiguration.java @@ -76,6 +76,8 @@ public class SmtpConfiguration implements SerializableAsXml { private Optional<Boolean> allowUnauthenticatedSender; private Optional<Boolean> bracketEnforcement; private Optional<String> authorizedAddresses; + private Optional<Boolean> authRequireSSL; + private Optional<String> saslMechanisms; private final ImmutableList.Builder<HookConfigurationEntry> additionalHooks; public Builder() { @@ -86,6 +88,8 @@ public class SmtpConfiguration implements SerializableAsXml { maxMessageSize = Optional.empty(); bracketEnforcement = Optional.empty(); allowUnauthenticatedSender = Optional.empty(); + authRequireSSL = Optional.empty(); + saslMechanisms = Optional.empty(); additionalHooks = ImmutableList.builder(); } @@ -135,6 +139,16 @@ public class SmtpConfiguration implements SerializableAsXml { return this; } + public Builder requireSSLForAuth(boolean requireSSL) { + this.authRequireSSL = Optional.of(requireSSL); + return this; + } + + public Builder withSaslMechanisms(String saslMechanisms) { + this.saslMechanisms = Optional.of(saslMechanisms); + return this; + } + public Builder relaxedIdentityVerification() { this.verifyIndentity = Optional.of(SMTPConfiguration.SenderVerificationMode.RELAXED); return this; @@ -158,6 +172,8 @@ public class SmtpConfiguration implements SerializableAsXml { allowUnauthenticatedSender.orElse(true), verifyIndentity.orElse(SMTPConfiguration.SenderVerificationMode.DISABLED), maxMessageSize.orElse(DEFAULT_DISABLED), + authRequireSSL.orElse(false), + saslMechanisms, additionalHooks.build()); } } @@ -173,6 +189,8 @@ public class SmtpConfiguration implements SerializableAsXml { private final boolean allowUnauthenticatedSenders; private final SMTPConfiguration.SenderVerificationMode verifyIndentity; private final String maxMessageSize; + private final boolean authRequireSSL; + private final Optional<String> saslMechanisms; private final ImmutableList<HookConfigurationEntry> additionalHooks; private SmtpConfiguration(Optional<String> authorizedAddresses, @@ -182,6 +200,8 @@ public class SmtpConfiguration implements SerializableAsXml { boolean allowUnauthenticatedSenders, SMTPConfiguration.SenderVerificationMode verifyIndentity, String maxMessageSize, + boolean authRequireSSL, + Optional<String> saslMechanisms, ImmutableList<HookConfigurationEntry> additionalHooks) { this.authorizedAddresses = authorizedAddresses; this.authRequired = authRequired; @@ -189,6 +209,8 @@ public class SmtpConfiguration implements SerializableAsXml { this.verifyIndentity = verifyIndentity; this.allowUnauthenticatedSenders = allowUnauthenticatedSenders; this.maxMessageSize = maxMessageSize; + this.authRequireSSL = authRequireSSL; + this.saslMechanisms = saslMechanisms; this.startTls = startTls; this.additionalHooks = additionalHooks; } @@ -202,6 +224,9 @@ public class SmtpConfiguration implements SerializableAsXml { scopes.put("verifyIdentity", verifyIndentity.toString()); scopes.put("forbidUnauthenticatedSenders", Boolean.toString(!allowUnauthenticatedSenders)); scopes.put("maxmessagesize", maxMessageSize); + scopes.put("authRequireSSL", authRequireSSL); + scopes.put("hasSaslMechanisms", saslMechanisms.isPresent()); + saslMechanisms.ifPresent(value -> scopes.put("saslMechanisms", value)); scopes.put("bracketEnforcement", bracketEnforcement); scopes.put("startTls", startTls); diff --git a/server/mailet/integration-testing/src/main/resources/smtpserver.xml b/server/mailet/integration-testing/src/main/resources/smtpserver.xml index e2a2c719fb..4cea11c9a0 100644 --- a/server/mailet/integration-testing/src/main/resources/smtpserver.xml +++ b/server/mailet/integration-testing/src/main/resources/smtpserver.xml @@ -39,8 +39,11 @@ {{/hasAuthorizedAddresses}} <auth> <required>{{forbidUnauthenticatedSenders}}</required> - <requireSSL>false</requireSSL> + <requireSSL>{{authRequireSSL}}</requireSSL> <plainAuthEnabled>true</plainAuthEnabled> +{{#hasSaslMechanisms}} + <saslMechanisms>{{saslMechanisms}}</saslMechanisms> +{{/hasSaslMechanisms}} </auth> <verifyIdentity>{{verifyIdentity}}</verifyIdentity> <maxmessagesize>{{maxmessagesize}}</maxmessagesize> @@ -83,6 +86,9 @@ <required>{{forbidUnauthenticatedSenders}}</required> <requireSSL>true</requireSSL> <plainAuthEnabled>true</plainAuthEnabled> +{{#hasSaslMechanisms}} + <saslMechanisms>{{saslMechanisms}}</saslMechanisms> +{{/hasSaslMechanisms}} </auth> <!-- Trust authenticated users --> <verifyIdentity>false</verifyIdentity> diff --git a/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/SmtpSaslMechanismIntegrationTest.java b/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/SmtpSaslMechanismIntegrationTest.java new file mode 100644 index 0000000000..3cd5188c12 --- /dev/null +++ b/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/SmtpSaslMechanismIntegrationTest.java @@ -0,0 +1,113 @@ +/**************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you under the Apache License, Version 2.0 (the * + * "License"); you may not use this file except in compliance * + * with the License. You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, * + * software distributed under the License is distributed on an * + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * + * KIND, either express or implied. See the License for the * + * specific language governing permissions and limitations * + * under the License. * + ****************************************************************/ + +package org.apache.james.mailets; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.apache.james.mailets.configuration.Constants.DEFAULT_DOMAIN; +import static org.apache.james.mailets.configuration.Constants.LOCALHOST_IP; +import static org.apache.james.mailets.configuration.Constants.PASSWORD; +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.File; +import java.util.Base64; + +import org.apache.commons.net.smtp.SMTPClient; +import org.apache.james.MemoryJamesServerMain; +import org.apache.james.mailets.configuration.CommonProcessors; +import org.apache.james.mailets.configuration.MailetConfiguration; +import org.apache.james.mailets.configuration.MailetContainer; +import org.apache.james.mailets.configuration.ProcessorConfiguration; +import org.apache.james.mailets.configuration.SmtpConfiguration; +import org.apache.james.modules.protocols.SmtpGuiceProbe; +import org.apache.james.probe.DataProbe; +import org.apache.james.utils.DataProbeImpl; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +class SmtpSaslMechanismIntegrationTest { + private static final String USER = "fromuser@" + DEFAULT_DOMAIN; + + private static String base64(String value) { + return Base64.getEncoder().encodeToString(value.getBytes(UTF_8)); + } + + private TemporaryJamesServer jamesServer; + + @BeforeEach + void setup(@TempDir File temporaryFolder) throws Exception { + jamesServer = TemporaryJamesServer.builder() + .withBase(MemoryJamesServerMain.SMTP_ONLY_MODULE) + .withMailetContainer(minimalMailetContainer()) + .withSmtpConfiguration(SmtpConfiguration.builder() + .doNotVerifyIdentity() + .requireSSLForAuth(true) + .withSaslMechanisms("LoginSaslMechanismFactory") + .build()) + .build(temporaryFolder); + jamesServer.start(); + + DataProbe dataProbe = jamesServer.getProbe(DataProbeImpl.class); + dataProbe.addDomain(DEFAULT_DOMAIN); + dataProbe.addUser(USER, PASSWORD); + } + + private MailetContainer.Builder minimalMailetContainer() { + return MailetContainer.builder() + .putProcessor(CommonProcessors.simpleRoot()) + .putProcessor(CommonProcessors.error()) + .putProcessor(CommonProcessors.rrtError()) + .putProcessor(ProcessorConfiguration.transport() + .addMailet(MailetConfiguration.BCC_STRIPPER) + .addMailet(MailetConfiguration.TO_BOUNCE)) + .putProcessor(CommonProcessors.bounces()); + } + + @AfterEach + void tearDown() { + jamesServer.shutdown(); + } + + @Test + void explicitlyConfiguredLoginSaslMechanismShouldAuthenticate() throws Exception { + SMTPClient smtpClient = new SMTPClient(); + try { + smtpClient.connect(LOCALHOST_IP, jamesServer.getProbe(SmtpGuiceProbe.class).getSmtpPort().getValue()); + + smtpClient.sendCommand("EHLO", "localhost"); + assertThat(smtpClient.getReplyCode()).isEqualTo(250); + + smtpClient.sendCommand("AUTH LOGIN"); + assertThat(smtpClient.getReplyCode()).isEqualTo(334); + + smtpClient.sendCommand(base64(USER)); + assertThat(smtpClient.getReplyCode()).isEqualTo(334); + + smtpClient.sendCommand(base64(PASSWORD)); + assertThat(smtpClient.getReplyCode()).isEqualTo(235); + } finally { + if (smtpClient.isConnected()) { + smtpClient.disconnect(); + } + } + } + +} --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
