You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by ma...@apache.org on 2020/05/15 09:04:46 UTC

[james-project] branch master updated: JAMES-3183 adds X-Originating-IP in network mailet matcher

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 <jh...@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 <ma...@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("test@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-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org