This is an automated email from the ASF dual-hosted git repository.

rcordier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit 816c9f80126c6767bcf81e81db827311b39b3de7
Author: Rene Cordier <rcord...@linagora.com>
AuthorDate: Fri Oct 11 17:17:30 2024 +0700

    JAMES-4032 Add validatedEntities field for DKIMHook
---
 .../servers/partials/configure/smtp-hooks.adoc     | 16 ++++---
 .../java/org/apache/james/smtpserver/DKIMHook.java | 53 ++++++++++++++++++----
 .../org/apache/james/smtpserver/DKIMHookTest.java  | 10 ++--
 3 files changed, 61 insertions(+), 18 deletions(-)

diff --git a/docs/modules/servers/partials/configure/smtp-hooks.adoc 
b/docs/modules/servers/partials/configure/smtp-hooks.adoc
index 83b821402d..afb1ce3e53 100644
--- a/docs/modules/servers/partials/configure/smtp-hooks.adoc
+++ b/docs/modules/servers/partials/configure/smtp-hooks.adoc
@@ -360,6 +360,7 @@ Supported configuration elements:
 
 - *forceCRLF*: Should CRLF be forced when computing body hashes.
 - *onlyForSenderDomain*: If specified, the DKIM checks are applied just for 
the emails whose MAIL FROM or from header specifies this domain. If 
unspecified, all emails are checked (default).
+- *validatedEntities*: If specified, comma separated values allowing granular 
checks on emails whose MAIL FROM (`envelope` option) or from header (`headers` 
option) specifies the domain defined in `onlyForSenderDomain`. If unspecified, 
defaults to `envelope,headers`.
 - *signatureRequired*: If DKIM signature is checked, the absence of signature 
will generate failure. Defaults to false.
 - *expectedDToken*: If DKIM signature is checked, the body should contain at 
least one DKIM signature with this d token. If unspecified, all d tokens are 
considered valid (default).
 
@@ -368,13 +369,14 @@ Example handlerchain configuration for `smtpserver.xml`:
 [source,xml]
 ....
 <handlerchain>
-       <handler class="org.apache.james.smtpserver.DKIMHook">
-               <forceCLRF>true</forceCLRF>
-               <onlyForSenderDomain>apache.org</onlyForSenderDomain>
-               <signatureRequired>true</signatureRequired>
-               <expectedDToken>apache.org</expectedDToken>
-       </handler>
-       <handler class="org.apache.james.smtpserver.CoreCmdHandlerLoader"/>
+    <handler class="org.apache.james.smtpserver.DKIMHook">
+        <forceCLRF>true</forceCLRF>
+        <onlyForSenderDomain>apache.org</onlyForSenderDomain>
+        <validatedEntities>envelope,headers</validatedEntities>
+        <signatureRequired>true</signatureRequired>
+        <expectedDToken>apache.org</expectedDToken>
+    </handler>
+    <handler class="org.apache.james.smtpserver.CoreCmdHandlerLoader"/>
 </handlerchain>
 ....
 
diff --git 
a/server/protocols/protocols-smtp-dkim/src/main/java/org/apache/james/smtpserver/DKIMHook.java
 
b/server/protocols/protocols-smtp-dkim/src/main/java/org/apache/james/smtpserver/DKIMHook.java
index cc8817ed53..cda884e4bd 100644
--- 
a/server/protocols/protocols-smtp-dkim/src/main/java/org/apache/james/smtpserver/DKIMHook.java
+++ 
b/server/protocols/protocols-smtp-dkim/src/main/java/org/apache/james/smtpserver/DKIMHook.java
@@ -22,7 +22,6 @@ package org.apache.james.smtpserver;
 import static org.apache.james.protocols.smtp.SMTPRetCode.AUTH_REQUIRED;
 import static org.apache.james.protocols.smtp.SMTPRetCode.LOCAL_ERROR;
 
-import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
@@ -52,6 +51,8 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 
 /**
@@ -90,8 +91,8 @@ public class DKIMHook implements JamesMessageHook {
 
     @FunctionalInterface
     interface DKIMCheckNeeded extends Predicate<Mail> {
-        static DKIMCheckNeeded or(DKIMCheckNeeded... checkNeededs) {
-            return mail -> Arrays.stream(checkNeededs)
+        static DKIMCheckNeeded or(ImmutableList<DKIMCheckNeeded> checkNeededs) 
{
+            return mail -> checkNeededs.stream()
                 .anyMatch(predicate -> predicate.test(mail));
         }
 
@@ -186,32 +187,54 @@ public class DKIMHook implements JamesMessageHook {
     }
 
     public static class Config {
+        public static final ImmutableList<ValidatedEntity> 
DEFAULT_VALIDATED_ENTITIES = ImmutableList.of(ValidatedEntity.envelope, 
ValidatedEntity.headers);
+
         public static Config parse(Configuration config) {
             return new Config(
                 config.getBoolean("forceCRLF", true),
                 config.getBoolean("signatureRequired", true),
                 Optional.ofNullable(config.getString("onlyForSenderDomain", 
null))
                     .map(Domain::of),
+                Optional.ofNullable(config.getString("validatedEntities", 
null))
+                    .map(entities -> Stream.of(entities.split(","))
+                        .map(ValidatedEntity::from)
+                        .collect(ImmutableList.toImmutableList()))
+                    .orElse(DEFAULT_VALIDATED_ENTITIES),
                 Optional.ofNullable(config.getString("expectedDToken", null)));
         }
 
+        public enum ValidatedEntity {
+            envelope,
+            headers;
+
+            static ValidatedEntity from(String rawValue) {
+                Preconditions.checkNotNull(rawValue);
+
+                return Stream.of(values())
+                    .filter(entity -> entity.name().equalsIgnoreCase(rawValue))
+                    .findAny()
+                    .orElseThrow(() -> new 
IllegalArgumentException(String.format("invalid validated entity '%s'", 
rawValue)));
+            }
+        }
+
         private final boolean forceCRLF;
         private final boolean signatureRequired;
         private final Optional<Domain> onlyForSenderDomain;
+        private final ImmutableList<ValidatedEntity> validatedEntities;
         private final Optional<String> expectedDToken;
 
-        public Config(boolean forceCRLF, boolean signatureRequired, 
Optional<Domain> onlyForSenderDomain, Optional<String> expectedDToken) {
+        public Config(boolean forceCRLF, boolean signatureRequired, 
Optional<Domain> onlyForSenderDomain,
+                      ImmutableList<ValidatedEntity> validatedEntities, 
Optional<String> expectedDToken) {
             this.forceCRLF = forceCRLF;
             this.signatureRequired = signatureRequired;
             this.onlyForSenderDomain = onlyForSenderDomain;
+            this.validatedEntities = validatedEntities;
             this.expectedDToken = expectedDToken;
         }
 
         DKIMCheckNeeded dkimCheckNeeded() {
             return onlyForSenderDomain
-                .map(domain -> DKIMCheckNeeded.or(
-                    DKIMCheckNeeded.onlyForSenderDomain(domain),
-                    DKIMCheckNeeded.onlyForHeaderFromDomain(domain)))
+                .map(domain -> 
DKIMCheckNeeded.or(computeDKIMChecksNeeded(domain)))
                 .orElse(DKIMCheckNeeded.ALL);
         }
 
@@ -222,12 +245,26 @@ public class DKIMHook implements JamesMessageHook {
                     .orElse(SignatureRecordValidation.ALLOW_ALL));
         }
 
+        private ImmutableList<DKIMCheckNeeded> computeDKIMChecksNeeded(Domain 
domain) {
+            return validatedEntities.stream()
+                .map(entity -> toDKIMCheck(entity, domain))
+                .collect(ImmutableList.toImmutableList());
+        }
+
+        private DKIMCheckNeeded toDKIMCheck(ValidatedEntity entity, Domain 
domain) {
+            return switch (entity) {
+                case envelope ->  DKIMCheckNeeded.onlyForSenderDomain(domain);
+                case headers -> 
DKIMCheckNeeded.onlyForHeaderFromDomain(domain);
+            };
+        }
+
         @Override
         public final boolean equals(Object o) {
             if (o instanceof Config config) {
                 return forceCRLF == config.forceCRLF
                     && signatureRequired == config.signatureRequired
                     && Objects.equals(onlyForSenderDomain, 
config.onlyForSenderDomain)
+                    && Objects.equals(validatedEntities, 
config.validatedEntities)
                     && Objects.equals(expectedDToken, config.expectedDToken);
             }
             return false;
@@ -235,7 +272,7 @@ public class DKIMHook implements JamesMessageHook {
 
         @Override
         public final int hashCode() {
-            return Objects.hash(forceCRLF, signatureRequired, 
onlyForSenderDomain, expectedDToken);
+            return Objects.hash(forceCRLF, signatureRequired, 
onlyForSenderDomain, validatedEntities, expectedDToken);
         }
     }
 
diff --git 
a/server/protocols/protocols-smtp-dkim/src/test/java/org/apache/james/smtpserver/DKIMHookTest.java
 
b/server/protocols/protocols-smtp-dkim/src/test/java/org/apache/james/smtpserver/DKIMHookTest.java
index fdad0bf7aa..dbb1184f9a 100644
--- 
a/server/protocols/protocols-smtp-dkim/src/test/java/org/apache/james/smtpserver/DKIMHookTest.java
+++ 
b/server/protocols/protocols-smtp-dkim/src/test/java/org/apache/james/smtpserver/DKIMHookTest.java
@@ -19,6 +19,7 @@
 
 package org.apache.james.smtpserver;
 
+import static 
org.apache.james.smtpserver.DKIMHook.Config.DEFAULT_VALIDATED_ENTITIES;
 import static org.assertj.core.api.Assertions.assertThat;
 
 import java.util.Optional;
@@ -29,6 +30,7 @@ import org.apache.james.core.MaybeSender;
 import org.apache.james.core.builder.MimeMessageBuilder;
 import org.apache.james.jdkim.tagvalue.SignatureRecordImpl;
 import org.apache.james.protocols.smtp.hook.HookReturnCode;
+import org.apache.james.smtpserver.DKIMHook.Config.ValidatedEntity;
 import org.apache.mailet.base.test.FakeMail;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
@@ -234,7 +236,8 @@ class DKIMHookTest {
             BaseHierarchicalConfiguration configuration = new 
BaseHierarchicalConfiguration();
 
             assertThat(DKIMHook.Config.parse(configuration))
-                .isEqualTo(new DKIMHook.Config(true, true, Optional.empty(), 
Optional.empty()));
+                .isEqualTo(new DKIMHook.Config(true, true, Optional.empty(),
+                    DEFAULT_VALIDATED_ENTITIES, Optional.empty()));
         }
 
         @Test
@@ -243,11 +246,12 @@ class DKIMHookTest {
             configuration.addProperty("forceCRLF", false);
             configuration.addProperty("signatureRequired", false);
             configuration.addProperty("onlyForSenderDomain", "linagora.com");
+            configuration.addProperty("validatedEntities", "envelope");
             configuration.addProperty("expectedDToken", "apache.org");
 
             assertThat(DKIMHook.Config.parse(configuration))
-                .isEqualTo(new DKIMHook.Config(false, false,
-                    Optional.of(Domain.of("linagora.com")), 
Optional.of("apache.org")));
+                .isEqualTo(new DKIMHook.Config(false, false, 
Optional.of(Domain.of("linagora.com")),
+                    ImmutableList.of(ValidatedEntity.envelope), 
Optional.of("apache.org")));
         }
     }
 }
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscr...@james.apache.org
For additional commands, e-mail: notifications-h...@james.apache.org

Reply via email to