This is an automated email from the ASF dual-hosted git repository. matthieu 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 f67aeee JAMES-3183 adds X-Originating-IP in network mailet matcher f67aeee is described below commit f67aeee227d6cd672fe5c7f8e0019c7dd2c8cd60 Author: Jean Helou <j...@codamens.fr> AuthorDate: Fri Sep 14 17:33:11 2018 +0200 JAMES-3183 adds X-Originating-IP in network mailet matcher The X-Originating-IP in network matcher checks the X-Originating-Ip header of the email. It can be used like the RemoteAddrInNetwork to make decisions on emails in the mailet pipeline. Co-authored-by: Matthieu Baechler <matth...@apache.org> --- server/mailet/mailets/pom.xml | 25 +++++ .../mailets/XOriginatingIpInNetwork.scala | 53 +++++++++ .../mailets/XOriginatingIpInNetworkSpec.scala | 125 +++++++++++++++++++++ 3 files changed, 203 insertions(+) diff --git a/server/mailet/mailets/pom.xml b/server/mailet/mailets/pom.xml index 401c505..6d88d25 100644 --- a/server/mailet/mailets/pom.xml +++ b/server/mailet/mailets/pom.xml @@ -32,6 +32,11 @@ <name>Apache James :: Server :: Mailets</name> + <properties> + <spec2.version>4.9.4</spec2.version> + </properties> + + <dependencies> <dependency> <groupId>${james.groupId}</groupId> @@ -225,6 +230,10 @@ <scope>test</scope> </dependency> <dependency> + <groupId>org.scala-lang</groupId> + <artifactId>scala-library</artifactId> + </dependency> + <dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> </dependency> @@ -233,6 +242,18 @@ <artifactId>slf4j-api</artifactId> </dependency> <dependency> + <groupId>org.specs2</groupId> + <artifactId>specs2-core_${scala.base}</artifactId> + <version>${spec2.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.specs2</groupId> + <artifactId>specs2-junit_${scala.base}</artifactId> + <version>${spec2.version}</version> + <scope>test</scope> + </dependency> + <dependency> <groupId>com.jakewharton.byteunits</groupId> <artifactId>byteunits</artifactId> <version>0.9.1</version> @@ -249,6 +270,10 @@ </configuration> </plugin> <plugin> + <groupId>net.alchim31.maven</groupId> + <artifactId>scala-maven-plugin</artifactId> + </plugin> + <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> diff --git a/server/mailet/mailets/src/main/scala/org/apache/james/transport/mailets/XOriginatingIpInNetwork.scala b/server/mailet/mailets/src/main/scala/org/apache/james/transport/mailets/XOriginatingIpInNetwork.scala new file mode 100644 index 0000000..ec17304 --- /dev/null +++ b/server/mailet/mailets/src/main/scala/org/apache/james/transport/mailets/XOriginatingIpInNetwork.scala @@ -0,0 +1,53 @@ +/** ************************************************************** + * 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.transport.mailets + +import java.util.{Collection => JavaCollection} + +import com.google.common.collect.ImmutableList +import org.apache.james.core.MailAddress +import org.apache.james.transport.matchers.AbstractNetworkMatcher +import org.apache.mailet.Mail + +object XOriginatingIpInNetwork { + val X_ORIGINATING_IP: String = "X-Originating-IP" +} + +/** + * <p> + * Checks the first X_ORIGINATING_IP IP address against a comma-delimited list + * of IP addresses, domain names or sub-nets. + * </p> + * <p> + * See [[AbstractNetworkMatcher]] for details on how to specify entries. + * </p> + */ +class XOriginatingIpInNetwork extends AbstractNetworkMatcher { + override def `match`(mail: Mail): JavaCollection[MailAddress] = matchOnOriginatingAddr(mail).getOrElse(ImmutableList.of()) + + def matchOnOriginatingAddr(mail: Mail): Option[JavaCollection[MailAddress]] = + Option(mail.getMessage.getHeader(XOriginatingIpInNetwork.X_ORIGINATING_IP)) + .flatMap(_.headOption) + .map(normalizeIP) + .filter(matchNetwork) + .map(_ => mail.getRecipients) + + private def normalizeIP(ip: String): String = ip.replace("[", "").replace("]", "") +} diff --git a/server/mailet/mailets/src/test/scala/org/apache/james/transport/mailets/XOriginatingIpInNetworkSpec.scala b/server/mailet/mailets/src/test/scala/org/apache/james/transport/mailets/XOriginatingIpInNetworkSpec.scala new file mode 100644 index 0000000..f7dd673 --- /dev/null +++ b/server/mailet/mailets/src/test/scala/org/apache/james/transport/mailets/XOriginatingIpInNetworkSpec.scala @@ -0,0 +1,125 @@ +/** ************************************************************** + * 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.transport.mailets + +import org.apache.james.core.MailAddress +import org.apache.james.core.builder.MimeMessageBuilder +import org.apache.james.dnsservice.api.{DNSService, InMemoryDNSService} +import org.apache.james.transport.mailets.XOriginatingIpInNetwork.X_ORIGINATING_IP +import org.apache.mailet.base.test.{FakeMail, FakeMatcherConfig} +import org.junit.runner.RunWith +import org.specs2.matcher.Matchers +import org.specs2.mutable.Specification +import org.specs2.runner.JUnitRunner + +import scala.jdk.CollectionConverters._ + +@RunWith(classOf[JUnitRunner]) +class XOriginatingIpInNetworkSpec extends Specification with Matchers { + val dnsServer: DNSService = + new InMemoryDNSService() + .registerMxRecord("192.168.0.1", "192.168.0.1") + .registerMxRecord("192.168.200.1", "192.168.200.1") + .registerMxRecord("192.168.200.0", "192.168.200.0") + .registerMxRecord("255.255.255.0", "255.255.255.0") + val matcherConfig: FakeMatcherConfig = + FakeMatcherConfig.builder + .matcherName("AllowedNetworkIs") + .condition("192.168.200.0/24") + .build + + "RemoteAddrOrOriginatingIpInNetwork" should { + val testRecipient = new MailAddress("t...@james.apache.org") + val matcher = new XOriginatingIpInNetwork() + matcher.setDNSService(dnsServer) + matcher.init(matcherConfig) + + s"match when ip of header $X_ORIGINATING_IP is on the same network" in { + val fakeMail = + FakeMail.builder + .name("mailname") + .recipient(testRecipient) + .remoteAddr("10.0.0.1") + .mimeMessage( + MimeMessageBuilder.mimeMessageBuilder() + .addToRecipient(testRecipient.toInternetAddress) + .addHeader(X_ORIGINATING_IP,"192.168.200.1") + .build()) + .build + + val actual = matcher.`match`(fakeMail).asScala + + actual must contain(exactly(testRecipient)) + } + + s"match when ip of header $X_ORIGINATING_IP is between brackets" in { + val fakeMail = + FakeMail.builder + .name("mailname") + .recipient(testRecipient) + .remoteAddr("10.0.0.1") + .mimeMessage( + MimeMessageBuilder.mimeMessageBuilder() + .addToRecipient(testRecipient.toInternetAddress) + .addHeader(X_ORIGINATING_IP,"[192.168.200.1]") + .build()) + .build + + val actual = matcher.`match`(fakeMail).asScala + + actual must contain(exactly(testRecipient)) + } + + s"not match when ip of header $X_ORIGINATING_IP is not on the same network" in { + val fakeMail = + FakeMail.builder + .name("mailname") + .recipient(testRecipient) + .remoteAddr("10.0.0.1") + .mimeMessage( + MimeMessageBuilder.mimeMessageBuilder() + .addToRecipient(testRecipient.toInternetAddress) + .addHeader(X_ORIGINATING_IP,"10.0.0.2") + .build()) + .build + + val actual = matcher.`match`(fakeMail).asScala + + actual must beEmpty + } + + s"not match when header $X_ORIGINATING_IP is missing" in { + val fakeMail = + FakeMail.builder + .name("mailname") + .recipient(testRecipient) + .remoteAddr("10.0.0.1") + .mimeMessage( + MimeMessageBuilder.mimeMessageBuilder() + .addToRecipient(testRecipient.toInternetAddress) + .build()) + .build + + val actual = matcher.`match`(fakeMail).asScala + + actual must beEmpty + } + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: server-dev-unsubscr...@james.apache.org For additional commands, e-mail: server-dev-h...@james.apache.org