This is an automated email from the ASF dual-hosted git repository. btellier pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/james-project.git
The following commit(s) were added to refs/heads/master by this push: new 5a62eceb21 [ENHANCEMENT] Integration tests for MaxRcptHandler (#2448) 5a62eceb21 is described below commit 5a62eceb21ff8fd7b34bff387e273f8dc7b8d69e Author: florentos17 <fazav...@linagora.com> AuthorDate: Tue Oct 15 09:04:35 2024 +0200 [ENHANCEMENT] Integration tests for MaxRcptHandler (#2448) --- .../smtp/core/fastfail/MaxRcptHandler.java | 2 +- .../mailets/configuration/SmtpConfiguration.java | 50 ++++++++-- .../src/main/resources/smtpserver.xml | 12 ++- .../configuration/SmtpConfigurationTest.java | 30 ++++++ .../apache/james/smtp/SmtpMaxRcptHandlerTest.java | 108 +++++++++++++++++++++ .../james/smtpserver/fastfail/MaxRcptHandler.java | 2 +- 6 files changed, 190 insertions(+), 14 deletions(-) diff --git a/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/core/fastfail/MaxRcptHandler.java b/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/core/fastfail/MaxRcptHandler.java index c3ae9b467b..632d69be5e 100644 --- a/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/core/fastfail/MaxRcptHandler.java +++ b/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/core/fastfail/MaxRcptHandler.java @@ -45,7 +45,7 @@ public class MaxRcptHandler implements RcptHook { /** - * Set the max rcpt for wich should be accepted + * Set the max rcpt for which a mail should be accepted * * @param maxRcpt * The max rcpt count 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 743754f46b..39088fa329 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 @@ -27,6 +27,8 @@ import java.io.StringReader; import java.io.Writer; import java.nio.charset.StandardCharsets; import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.Optional; import org.apache.commons.io.IOUtils; @@ -44,9 +46,23 @@ public class SmtpConfiguration implements SerializableAsXml { static class HookConfigurationEntry { String hookFqcn; + Map<String, String> hookConfig; + + HookConfigurationEntry(String hookFqcn, Map<String, String> hookConfig) { + this.hookFqcn = hookFqcn; + this.hookConfig = hookConfig; + } HookConfigurationEntry(String hookFqcn) { this.hookFqcn = hookFqcn; + this.hookConfig = new HashMap<>(); + } + + private static Map<String, Object> asMustacheScopes(HookConfigurationEntry hook) { + Map<String, Object> hookScope = new HashMap<>(); + hookScope.put("hookFqcn", hook.hookFqcn); + hookScope.put("hookConfigAsXML", hook.hookConfig.entrySet()); + return hookScope; } } @@ -58,7 +74,7 @@ public class SmtpConfiguration implements SerializableAsXml { private Optional<SMTPConfiguration.SenderVerificationMode> verifyIndentity; private Optional<Boolean> bracketEnforcement; private Optional<String> authorizedAddresses; - private ImmutableList.Builder<HookConfigurationEntry> addittionalHooks; + private ImmutableList.Builder<HookConfigurationEntry> additionalHooks; public Builder() { authorizedAddresses = Optional.empty(); @@ -66,7 +82,7 @@ public class SmtpConfiguration implements SerializableAsXml { verifyIndentity = Optional.empty(); maxMessageSize = Optional.empty(); bracketEnforcement = Optional.empty(); - addittionalHooks = ImmutableList.builder(); + additionalHooks = ImmutableList.builder(); } public Builder withAutorizedAddresses(String authorizedAddresses) { @@ -111,7 +127,12 @@ public class SmtpConfiguration implements SerializableAsXml { } public Builder addHook(String hookFQCN) { - this.addittionalHooks.add(new HookConfigurationEntry(hookFQCN)); + this.additionalHooks.add(new HookConfigurationEntry(hookFQCN)); + return this; + } + + public Builder addHook(String hookFQCN, Map<String, String> hookConfig) { + this.additionalHooks.add(new HookConfigurationEntry(hookFQCN, hookConfig)); return this; } @@ -121,7 +142,7 @@ public class SmtpConfiguration implements SerializableAsXml { bracketEnforcement.orElse(true), verifyIndentity.orElse(SMTPConfiguration.SenderVerificationMode.DISABLED), maxMessageSize.orElse(DEFAULT_DISABLED), - addittionalHooks.build()); + additionalHooks.build()); } } @@ -134,16 +155,20 @@ public class SmtpConfiguration implements SerializableAsXml { private final boolean bracketEnforcement; private final SMTPConfiguration.SenderVerificationMode verifyIndentity; private final String maxMessageSize; - private final ImmutableList<HookConfigurationEntry> addittionalHooks; - - private SmtpConfiguration(Optional<String> authorizedAddresses, boolean authRequired, boolean bracketEnforcement, - SMTPConfiguration.SenderVerificationMode verifyIndentity, String maxMessageSize, ImmutableList<HookConfigurationEntry> addittionalHooks) { + private final ImmutableList<HookConfigurationEntry> additionalHooks; + + private SmtpConfiguration(Optional<String> authorizedAddresses, + boolean authRequired, + boolean bracketEnforcement, + SMTPConfiguration.SenderVerificationMode verifyIndentity, + String maxMessageSize, + ImmutableList<HookConfigurationEntry> additionalHooks) { this.authorizedAddresses = authorizedAddresses; this.authRequired = authRequired; this.bracketEnforcement = bracketEnforcement; this.verifyIndentity = verifyIndentity; this.maxMessageSize = maxMessageSize; - this.addittionalHooks = addittionalHooks; + this.additionalHooks = additionalHooks; } @Override @@ -155,7 +180,12 @@ public class SmtpConfiguration implements SerializableAsXml { scopes.put("verifyIdentity", verifyIndentity.toString()); scopes.put("maxmessagesize", maxMessageSize); scopes.put("bracketEnforcement", bracketEnforcement); - scopes.put("hooks", addittionalHooks); + + List<Map<String, Object>> additionalHooksWithConfig = additionalHooks.stream() + .map(HookConfigurationEntry::asMustacheScopes) + .collect(ImmutableList.toImmutableList()); + + scopes.put("hooks", additionalHooksWithConfig); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); Writer writer = new OutputStreamWriter(byteArrayOutputStream); diff --git a/server/mailet/integration-testing/src/main/resources/smtpserver.xml b/server/mailet/integration-testing/src/main/resources/smtpserver.xml index 4f0a039742..2d314ac085 100644 --- a/server/mailet/integration-testing/src/main/resources/smtpserver.xml +++ b/server/mailet/integration-testing/src/main/resources/smtpserver.xml @@ -49,7 +49,11 @@ <handler class="org.apache.james.smtpserver.fastfail.ValidRcptHandler"/> <handler class="org.apache.james.smtpserver.CoreCmdHandlerLoader"/> {{#hooks}} - <handler class="{{hookFqcn}}"/> + <handler class="{{hookFqcn}}"> + {{#hookConfigAsXML}} + <{{key}}>{{value}}</{{key}}> + {{/hookConfigAsXML}} + </handler> {{/hooks}} </handlerchain> <gracefulShutdown>false</gracefulShutdown> @@ -87,7 +91,11 @@ <handler class="org.apache.james.smtpserver.fastfail.ValidRcptHandler"/> <handler class="org.apache.james.smtpserver.CoreCmdHandlerLoader"/> {{#hooks}} - <handler class="{{hookFqcn}}"/> + <handler class="{{hookFqcn}}"> + {{#hookConfigAsXML}} + <{{key}}>{{value}}</{{key}}> + {{/hookConfigAsXML}} + </handler> {{/hooks}} </handlerchain> <gracefulShutdown>false</gracefulShutdown> diff --git a/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/configuration/SmtpConfigurationTest.java b/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/configuration/SmtpConfigurationTest.java index ef029cfd5a..58724e8bf2 100644 --- a/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/configuration/SmtpConfigurationTest.java +++ b/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/configuration/SmtpConfigurationTest.java @@ -24,6 +24,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.xmlunit.matchers.EvaluateXPathMatcher.hasXPath; import java.io.IOException; +import java.util.Map; import javax.xml.transform.Source; @@ -180,4 +181,33 @@ public class SmtpConfigurationTest { hasXPath("/smtpservers/smtpserver/verifyIdentity/text()", is("DISABLED"))); } + + @Test + public void addHookShouldRegisterHookWithoutConfig() throws IOException { + String hookFqcn = "com.example.hooks.MyCustomHook"; + + SmtpConfiguration configuration = SmtpConfiguration.builder() + .addHook(hookFqcn) + .build(); + + assertThat(configuration.serializeAsXml(), + hasXPath("/smtpservers/smtpserver/handlerchain/handler[@class='" + hookFqcn + "']/@class", is(hookFqcn))); + } + + @Test + public void addHookShouldRegisterHookWithConfig() throws IOException { + String hookFqcn = "com.example.hooks.MyCustomHook"; + Map<String, String> hookConfig = Map.of("param1", "value1", "param2", "value2"); + + SmtpConfiguration configuration = SmtpConfiguration.builder() + .addHook(hookFqcn, hookConfig) + .build(); + + String xmlOutput = configuration.serializeAsXml(); + assertThat(xmlOutput, hasXPath("/smtpservers/smtpserver/handlerchain/handler[@class='" + hookFqcn + "']/@class", is(hookFqcn))); + assertThat(xmlOutput, hasXPath("/smtpservers/smtpserver/handlerchain/handler[@class='" + hookFqcn + "']/param1/text()", is("value1"))); + assertThat(xmlOutput, hasXPath("/smtpservers/smtpserver/handlerchain/handler[@class='" + hookFqcn + "']/param2/text()", is("value2"))); + } + + } diff --git a/server/mailet/integration-testing/src/test/java/org/apache/james/smtp/SmtpMaxRcptHandlerTest.java b/server/mailet/integration-testing/src/test/java/org/apache/james/smtp/SmtpMaxRcptHandlerTest.java new file mode 100644 index 0000000000..77e5fbb37e --- /dev/null +++ b/server/mailet/integration-testing/src/test/java/org/apache/james/smtp/SmtpMaxRcptHandlerTest.java @@ -0,0 +1,108 @@ +/**************************************************************** + * 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.smtp; + +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.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import java.io.File; +import java.util.List; +import java.util.Map; +import java.util.stream.IntStream; + +import org.apache.james.mailets.TemporaryJamesServer; +import org.apache.james.mailets.configuration.SmtpConfiguration; +import org.apache.james.modules.protocols.SmtpGuiceProbe; +import org.apache.james.probe.DataProbe; +import org.apache.james.smtpserver.fastfail.MaxRcptHandler; +import org.apache.james.utils.DataProbeImpl; +import org.apache.james.utils.SMTPMessageSender; +import org.apache.james.utils.SMTPSendingException; +import org.apache.james.utils.SmtpSendingStep; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.api.io.TempDir; + +import com.github.fge.lambdas.Throwing; +import com.google.common.collect.ImmutableList; + +class SmtpMaxRcptHandlerTest { + private static final String USER = "user@" + DEFAULT_DOMAIN; + private static final Integer DEFAULT_MAX_RCPT = 50; + + @RegisterExtension + public SMTPMessageSender messageSender = new SMTPMessageSender(DEFAULT_DOMAIN); + + private TemporaryJamesServer jamesServer; + + @BeforeEach + public void createJamesServer(@TempDir File temporaryFolder) throws Exception { + SmtpConfiguration.Builder smtpConfiguration = SmtpConfiguration.builder() + .doNotVerifyIdentity() + .addHook(MaxRcptHandler.class.getName(), + Map.of("maxRcpt", DEFAULT_MAX_RCPT.toString())); + + jamesServer = TemporaryJamesServer.builder() + .withSmtpConfiguration(smtpConfiguration) + .build(temporaryFolder); + jamesServer.start(); + + DataProbe dataProbe = jamesServer.getProbe(DataProbeImpl.class); + dataProbe.addDomain(DEFAULT_DOMAIN); + dataProbe.addUser(USER, PASSWORD); + IntStream.range(0, DEFAULT_MAX_RCPT + 1).forEach(Throwing.intConsumer(( + i -> dataProbe.addUser("recipient" + i + "@" + DEFAULT_DOMAIN, PASSWORD)))); + } + + @AfterEach + void tearDown() { + if (jamesServer != null) { + jamesServer.shutdown(); + } + } + + @Test + void messageShouldNotBeAcceptedWhenMaxRcptHandlerExceeded() throws Exception { + assertThatThrownBy(() -> + messageSender.connect(LOCALHOST_IP, jamesServer.getProbe(SmtpGuiceProbe.class).getSmtpPort()) + .authenticate(USER, PASSWORD) + .sendMessageWithHeaders(USER, getRecipients(DEFAULT_MAX_RCPT + 1), "message")) + .isEqualTo(new SMTPSendingException(SmtpSendingStep.RCPT, "452 4.5.3 Requested action not taken: max recipients reached\n")); + } + + @Test + void messageShouldBeAcceptedWhenMaxRcptHandlerWithinLimit() throws Exception { + assertDoesNotThrow(() -> + messageSender.connect(LOCALHOST_IP, jamesServer.getProbe(SmtpGuiceProbe.class).getSmtpPort()) + .authenticate(USER, PASSWORD) + .sendMessageWithHeaders(USER, getRecipients(DEFAULT_MAX_RCPT), "message")); + } + + private List<String> getRecipients(Integer n) { + return IntStream.range(0, n) + .mapToObj(i -> "recipient" + i + "@" + DEFAULT_DOMAIN) + .collect(ImmutableList.toImmutableList()); + } +} diff --git a/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/fastfail/MaxRcptHandler.java b/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/fastfail/MaxRcptHandler.java index 8c957601af..b85340b42f 100644 --- a/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/fastfail/MaxRcptHandler.java +++ b/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/fastfail/MaxRcptHandler.java @@ -27,6 +27,6 @@ public class MaxRcptHandler extends org.apache.james.protocols.smtp.core.fastfai @Override public void init(Configuration config) throws ConfigurationException { int maxRcpt = config.getInt("maxRcpt", 0); - setMaxRcpt(maxRcpt); + setMaxRcpt(maxRcpt); } } --------------------------------------------------------------------- To unsubscribe, e-mail: notifications-unsubscr...@james.apache.org For additional commands, e-mail: notifications-h...@james.apache.org