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 9fbcaca010f10c74d5b2f9d7f58c0be54de92786
Author: Benoit TELLIER <[email protected]>
AuthorDate: Wed Jun 19 15:10:01 2024 +0200

    JAMES-4044 Handle both presence and value matching
---
 docs/modules/servers/partials/LDAPMatchers.adoc    | 30 +++++++++-
 server/mailet/ldap/README.md                       | 26 +++++++-
 .../james/transport/matchers/HasLDAPAttribute.java | 37 +++++++-----
 .../transport/matchers/SenderHasLDAPAttribute.java | 33 +++++++----
 .../transport/matchers/HasLDAPAttributeTest.java   | 67 ++++++++++++++++++++-
 .../matchers/SenderHasLDAPAttributeTest.java       | 69 ++++++++++++++++++++++
 6 files changed, 228 insertions(+), 34 deletions(-)

diff --git a/docs/modules/servers/partials/LDAPMatchers.adoc 
b/docs/modules/servers/partials/LDAPMatchers.adoc
index a202ba65ec..44980ca084 100644
--- a/docs/modules/servers/partials/LDAPMatchers.adoc
+++ b/docs/modules/servers/partials/LDAPMatchers.adoc
@@ -24,18 +24,42 @@ Then copy the JAR located in `/target` into the 
`extensions-jars` folder of your
 
 The LDAP settings are reused from the `usersrepository.xml` configuration file.
 
-Then LDAP matchers can be configured within `mailetcontainer.xml`. For 
instance:
+Then LDAP matchers can be configured within `mailetcontainer.xml`.
+
+Then LDAP matchers can be configured within `mailetcontainer.xml`.
+
+For instance:
 
 ....xml
+<!-- Matches recipients that have the following attribute with the specified 
value-->
 <mailet matcher="HasLDAPAttibute=description:blocked" class="Null">
-    
+
 </mailet>
 ....
 
 or
 
 ....xml
+<!-- Matches sender that have the following attribute with the specified 
value-->
 <mailet matcher="SenderHasLDAPAttibute=description:blocked" class="Null">
-    
+
+</mailet>
+....
+
+or
+
+....xml
+<!-- Matches recipients that have the following attribute regardless of the 
actual value-->
+<mailet matcher="HasLDAPAttibute=description" class="Null">
+
+</mailet>
+....
+
+or
+
+....xml
+<!-- Matches sender that have the following attribute regardless of the actual 
value-->
+<mailet matcher="SenderHasLDAPAttibute=description" class="Null">
+
 </mailet>
 ....
diff --git a/server/mailet/ldap/README.md b/server/mailet/ldap/README.md
index 16c21dbd83..b2ccb82d26 100644
--- a/server/mailet/ldap/README.md
+++ b/server/mailet/ldap/README.md
@@ -24,18 +24,40 @@ Then copy the JAR located in `/target` into the 
`extensions-jars` folder of your
 
 The LDAP settings are reused from the `usersrepository.xml` configuration file.
 
-Then LDAP matchers can be configured within `mailetcontainer.xml`. For 
instance:
+Then LDAP matchers can be configured within `mailetcontainer.xml`. 
+
+For instance:
 
 ```xml
+<!-- Matches recipients that have the following attribute with the specified 
value-->
 <mailet matcher="HasLDAPAttibute=description:blocked" class="Null">
-    
+
 </mailet>
 ```
 
 or
 
 ```xml
+<!-- Matches sender that have the following attribute with the specified 
value-->
 <mailet matcher="SenderHasLDAPAttibute=description:blocked" class="Null">
+
+</mailet>
+```
+
+or
+
+```xml
+<!-- Matches recipients that have the following attribute regardless of the 
actual value-->
+<mailet matcher="HasLDAPAttibute=description" class="Null">
+    
+</mailet>
+```
+
+or
+
+```xml
+<!-- Matches sender that have the following attribute regardless of the actual 
value-->
+<mailet matcher="SenderHasLDAPAttibute=description" class="Null">
     
 </mailet>
 ```
diff --git 
a/server/mailet/ldap/src/main/java/org/apache/james/transport/matchers/HasLDAPAttribute.java
 
b/server/mailet/ldap/src/main/java/org/apache/james/transport/matchers/HasLDAPAttribute.java
index f922ff484c..ce529f5edd 100644
--- 
a/server/mailet/ldap/src/main/java/org/apache/james/transport/matchers/HasLDAPAttribute.java
+++ 
b/server/mailet/ldap/src/main/java/org/apache/james/transport/matchers/HasLDAPAttribute.java
@@ -41,6 +41,7 @@ import com.unboundid.ldap.sdk.LDAPConnectionPool;
 import com.unboundid.ldap.sdk.LDAPException;
 import com.unboundid.ldap.sdk.LDAPSearchException;
 import com.unboundid.ldap.sdk.SearchResult;
+import com.unboundid.ldap.sdk.SearchResultEntry;
 import com.unboundid.ldap.sdk.SearchScope;
 
 public class HasLDAPAttribute extends GenericMatcher {
@@ -49,7 +50,7 @@ public class HasLDAPAttribute extends GenericMatcher {
     private final Filter objectClassFilter;
     private final Optional<Filter> userExtraFilter;
     private String attributeName;
-    private String attributeValue;
+    private Optional<String> attributeValue;
     private String[] attributes;
 
     @Inject
@@ -67,16 +68,17 @@ public class HasLDAPAttribute extends GenericMatcher {
         String condition = getCondition().trim();
         int commaPosition = condition.indexOf(':');
 
-        if (-1 == commaPosition) {
-            throw new MessagingException("Syntax Error. Missing ':'.");
+        if (commaPosition == -1) {
+            attributeName = condition;
+            attributeValue = Optional.empty();
+        } else {
+            if (commaPosition == 0) {
+                throw new MessagingException("Syntax Error. Missing attribute 
name.");
+            }
+            attributeName = condition.substring(0, commaPosition).trim();
+            attributeValue = Optional.of(condition.substring(commaPosition + 
1).trim());
         }
 
-        if (0 == commaPosition) {
-            throw new MessagingException("Syntax Error. Missing attribute 
name.");
-        }
-
-        attributeName = condition.substring(0, commaPosition).trim();
-        attributeValue = condition.substring(commaPosition + 1).trim();
         attributes = ImmutableSet.builder()
             .add(configuration.getReturnedAttributes())
             .add(attributeName)
@@ -98,17 +100,22 @@ public class HasLDAPAttribute extends GenericMatcher {
                 createFilter(rcpt.asString(), 
configuration.getUserIdAttribute()),
                 attributes);
 
-            return searchResult.getSearchEntries().stream()
-                .anyMatch(entry -> 
Optional.ofNullable(entry.getAttribute(attributeName))
-                    .map(Attribute::getValue)
-                    .map(attributeValue::equals)
-                    .orElse(false));
-
+            return searchResult.getSearchEntries()
+                .stream()
+                .anyMatch(this::hasAttribute);
         } catch (LDAPSearchException e) {
             throw new RuntimeException("Failed searching LDAP", e);
         }
     }
 
+    private boolean hasAttribute(SearchResultEntry entry) {
+        return attributeValue.map(value -> 
Optional.ofNullable(entry.getAttribute(attributeName))
+            .map(Attribute::getValue)
+            .map(value::equals)
+            .orElse(false))
+            .orElseGet(() -> entry.hasAttribute(attributeName));
+    }
+
     private Filter createFilter(String retrievalName, String 
ldapUserRetrievalAttribute) {
         Filter specificUserFilter = 
Filter.createEqualityFilter(ldapUserRetrievalAttribute, retrievalName);
         return userExtraFilter
diff --git 
a/server/mailet/ldap/src/main/java/org/apache/james/transport/matchers/SenderHasLDAPAttribute.java
 
b/server/mailet/ldap/src/main/java/org/apache/james/transport/matchers/SenderHasLDAPAttribute.java
index e4103d4f00..54915d4ff3 100644
--- 
a/server/mailet/ldap/src/main/java/org/apache/james/transport/matchers/SenderHasLDAPAttribute.java
+++ 
b/server/mailet/ldap/src/main/java/org/apache/james/transport/matchers/SenderHasLDAPAttribute.java
@@ -41,6 +41,7 @@ import com.unboundid.ldap.sdk.LDAPConnectionPool;
 import com.unboundid.ldap.sdk.LDAPException;
 import com.unboundid.ldap.sdk.LDAPSearchException;
 import com.unboundid.ldap.sdk.SearchResult;
+import com.unboundid.ldap.sdk.SearchResultEntry;
 import com.unboundid.ldap.sdk.SearchScope;
 
 public class SenderHasLDAPAttribute extends GenericMatcher {
@@ -49,7 +50,7 @@ public class SenderHasLDAPAttribute extends GenericMatcher {
     private final Filter objectClassFilter;
     private final Optional<Filter> userExtraFilter;
     private String attributeName;
-    private String attributeValue;
+    private Optional<String> attributeValue;
     private String[] attributes;
 
     @Inject
@@ -67,16 +68,17 @@ public class SenderHasLDAPAttribute extends GenericMatcher {
         String condition = getCondition().trim();
         int commaPosition = condition.indexOf(':');
 
-        if (-1 == commaPosition) {
-            throw new MessagingException("Syntax Error. Missing ':'.");
+        if (commaPosition == -1) {
+            attributeName = condition;
+            attributeValue = Optional.empty();
+        } else {
+            if (commaPosition == 0) {
+                throw new MessagingException("Syntax Error. Missing attribute 
name.");
+            }
+            attributeName = condition.substring(0, commaPosition).trim();
+            attributeValue = Optional.of(condition.substring(commaPosition + 
1).trim());
         }
 
-        if (0 == commaPosition) {
-            throw new MessagingException("Syntax Error. Missing attribute 
name.");
-        }
-
-        attributeName = condition.substring(0, commaPosition).trim();
-        attributeValue = condition.substring(commaPosition + 1).trim();
         attributes = ImmutableSet.builder()
             .add(configuration.getReturnedAttributes())
             .add(attributeName)
@@ -102,16 +104,21 @@ public class SenderHasLDAPAttribute extends 
GenericMatcher {
                 attributes);
 
             return searchResult.getSearchEntries().stream()
-                .anyMatch(entry -> 
Optional.ofNullable(entry.getAttribute(attributeName))
-                    .map(Attribute::getValue)
-                    .map(attributeValue::equals)
-                    .orElse(false));
+                .anyMatch(this::hasAttribute);
 
         } catch (LDAPSearchException e) {
             throw new RuntimeException("Failed searching LDAP", e);
         }
     }
 
+    private boolean hasAttribute(SearchResultEntry entry) {
+        return attributeValue.map(value -> 
Optional.ofNullable(entry.getAttribute(attributeName))
+                .map(Attribute::getValue)
+                .map(value::equals)
+                .orElse(false))
+            .orElseGet(() -> entry.hasAttribute(attributeName));
+    }
+
     private Filter createFilter(String retrievalName, String 
ldapUserRetrievalAttribute) {
         Filter specificUserFilter = 
Filter.createEqualityFilter(ldapUserRetrievalAttribute, retrievalName);
         return userExtraFilter
diff --git 
a/server/mailet/ldap/src/test/java/org/apache/james/transport/matchers/HasLDAPAttributeTest.java
 
b/server/mailet/ldap/src/test/java/org/apache/james/transport/matchers/HasLDAPAttributeTest.java
index f1cdddaca4..7be38f426a 100644
--- 
a/server/mailet/ldap/src/test/java/org/apache/james/transport/matchers/HasLDAPAttributeTest.java
+++ 
b/server/mailet/ldap/src/test/java/org/apache/james/transport/matchers/HasLDAPAttributeTest.java
@@ -22,10 +22,14 @@ package org.apache.james.transport.matchers;
 import static org.apache.james.user.ldap.DockerLdapSingleton.ADMIN;
 import static org.apache.james.user.ldap.DockerLdapSingleton.ADMIN_PASSWORD;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatCode;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 import java.util.Collection;
 import java.util.Optional;
 
+import jakarta.mail.MessagingException;
+
 import org.apache.commons.configuration2.HierarchicalConfiguration;
 import org.apache.commons.configuration2.plist.PropertyListConfiguration;
 import org.apache.commons.configuration2.tree.ImmutableNode;
@@ -55,7 +59,7 @@ class HasLDAPAttributeTest {
     }
 
     @Test
-    void shouldReturnRecipientWhenHasAttribute() throws Exception {
+    void shouldReturnRecipientWhenHasAttributeWithValue() throws Exception {
         HasLDAPAttribute testee = new 
HasLDAPAttribute(LdapRepositoryConfiguration.from(ldapRepositoryConfigurationWithVirtualHosting(ldapContainer)));
         FakeMatcherConfig matcherConfig = FakeMatcherConfig.builder()
             .matcherName("HasLDAPAttribute")
@@ -72,6 +76,67 @@ class HasLDAPAttributeTest {
         assertThat(matched).containsOnly(recipient);
     }
 
+    @Test
+    void shouldThrowWhenPrefixedWithDelimiter() throws Exception {
+        HasLDAPAttribute testee = new 
HasLDAPAttribute(LdapRepositoryConfiguration.from(ldapRepositoryConfigurationWithVirtualHosting(ldapContainer)));
+        FakeMatcherConfig matcherConfig = FakeMatcherConfig.builder()
+            .matcherName("HasLDAPAttribute")
+            .condition(":abcdef")
+            .build();
+
+        assertThatThrownBy(() -> testee.init(matcherConfig))
+            .isInstanceOf(MessagingException.class);
+    }
+
+    @Test
+    void shouldNotThrowWhenValueContainsDelimiter() throws Exception {
+        HasLDAPAttribute testee = new 
HasLDAPAttribute(LdapRepositoryConfiguration.from(ldapRepositoryConfigurationWithVirtualHosting(ldapContainer)));
+        FakeMatcherConfig matcherConfig = FakeMatcherConfig.builder()
+            .matcherName("HasLDAPAttribute")
+            .condition("description:abc:def")
+            .build();
+        testee.init(matcherConfig);
+
+        assertThatCode(() -> testee.init(matcherConfig))
+            .doesNotThrowAnyException();;
+    }
+
+    @Test
+    void shouldReturnRecipientWhenHasAttribute() throws Exception {
+        HasLDAPAttribute testee = new 
HasLDAPAttribute(LdapRepositoryConfiguration.from(ldapRepositoryConfigurationWithVirtualHosting(ldapContainer)));
+        FakeMatcherConfig matcherConfig = FakeMatcherConfig.builder()
+            .matcherName("HasLDAPAttribute")
+            .condition("description")
+            .build();
+        testee.init(matcherConfig);
+
+        MailAddress recipient = new MailAddress("[email protected]");
+        Collection<MailAddress> matched = testee.match(FakeMail.builder()
+            .name("default-id")
+            .recipient(recipient)
+            .build());
+
+        assertThat(matched).containsOnly(recipient);
+    }
+
+    @Test
+    void shouldNotReturnRecipientWhenDoesNotHaveAttibure() throws Exception {
+        HasLDAPAttribute testee = new 
HasLDAPAttribute(LdapRepositoryConfiguration.from(ldapRepositoryConfigurationWithVirtualHosting(ldapContainer)));
+        FakeMatcherConfig matcherConfig = FakeMatcherConfig.builder()
+            .matcherName("HasLDAPAttribute")
+            .condition("descriptionaa")
+            .build();
+        testee.init(matcherConfig);
+
+        MailAddress recipient = new MailAddress("[email protected]");
+        Collection<MailAddress> matched = testee.match(FakeMail.builder()
+            .name("default-id")
+            .recipient(recipient)
+            .build());
+
+        assertThat(matched).isEmpty();
+    }
+
     @Test
     void shouldReturnEmptyWhenHasNoAttribute() throws Exception {
         HasLDAPAttribute testee = new 
HasLDAPAttribute(LdapRepositoryConfiguration.from(ldapRepositoryConfigurationWithVirtualHosting(ldapContainer)));
diff --git 
a/server/mailet/ldap/src/test/java/org/apache/james/transport/matchers/SenderHasLDAPAttributeTest.java
 
b/server/mailet/ldap/src/test/java/org/apache/james/transport/matchers/SenderHasLDAPAttributeTest.java
index 02ebabc2b3..cbf1cfc500 100644
--- 
a/server/mailet/ldap/src/test/java/org/apache/james/transport/matchers/SenderHasLDAPAttributeTest.java
+++ 
b/server/mailet/ldap/src/test/java/org/apache/james/transport/matchers/SenderHasLDAPAttributeTest.java
@@ -22,10 +22,14 @@ package org.apache.james.transport.matchers;
 import static org.apache.james.user.ldap.DockerLdapSingleton.ADMIN;
 import static org.apache.james.user.ldap.DockerLdapSingleton.ADMIN_PASSWORD;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatCode;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 import java.util.Collection;
 import java.util.Optional;
 
+import jakarta.mail.MessagingException;
+
 import org.apache.commons.configuration2.HierarchicalConfiguration;
 import org.apache.commons.configuration2.plist.PropertyListConfiguration;
 import org.apache.commons.configuration2.tree.ImmutableNode;
@@ -54,6 +58,71 @@ class SenderHasLDAPAttributeTest {
         ldapContainer.stop();
     }
 
+    @Test
+    void shouldThrowWhenPrefixedWithDelimiter() throws Exception {
+        SenderHasLDAPAttribute testee = new 
SenderHasLDAPAttribute(LdapRepositoryConfiguration.from(ldapRepositoryConfigurationWithVirtualHosting(ldapContainer)));
+        FakeMatcherConfig matcherConfig = FakeMatcherConfig.builder()
+            .matcherName("HasLDAPAttribute")
+            .condition(":abcdef")
+            .build();
+
+        assertThatThrownBy(() -> testee.init(matcherConfig))
+            .isInstanceOf(MessagingException.class);
+    }
+
+    @Test
+    void shouldNotThrowWhenValueContainsDelimiter() throws Exception {
+        SenderHasLDAPAttribute testee = new 
SenderHasLDAPAttribute(LdapRepositoryConfiguration.from(ldapRepositoryConfigurationWithVirtualHosting(ldapContainer)));
+        FakeMatcherConfig matcherConfig = FakeMatcherConfig.builder()
+            .matcherName("HasLDAPAttribute")
+            .condition("description:abc:def")
+            .build();
+        testee.init(matcherConfig);
+
+        assertThatCode(() -> testee.init(matcherConfig))
+            .doesNotThrowAnyException();;
+    }
+
+    @Test
+    void shouldReturnRecipientWhenHasAttribute() throws Exception {
+        SenderHasLDAPAttribute testee = new 
SenderHasLDAPAttribute(LdapRepositoryConfiguration.from(ldapRepositoryConfigurationWithVirtualHosting(ldapContainer)));
+        FakeMatcherConfig matcherConfig = FakeMatcherConfig.builder()
+            .matcherName("SenderHasLDAPAttribute")
+            .condition("description")
+            .build();
+        testee.init(matcherConfig);
+
+        MailAddress sender = new MailAddress("[email protected]");
+        MailAddress recipient = new MailAddress("[email protected]");
+        Collection<MailAddress> matched = testee.match(FakeMail.builder()
+            .name("default-id")
+            .sender(sender)
+            .recipient(recipient)
+            .build());
+
+        assertThat(matched).containsOnly(recipient);
+    }
+
+    @Test
+    void shouldNotReturnRecipientWhenDoesNotHaveAttribute() throws Exception {
+        SenderHasLDAPAttribute testee = new 
SenderHasLDAPAttribute(LdapRepositoryConfiguration.from(ldapRepositoryConfigurationWithVirtualHosting(ldapContainer)));
+        FakeMatcherConfig matcherConfig = FakeMatcherConfig.builder()
+            .matcherName("SenderHasLDAPAttribute")
+            .condition("descriptionaa")
+            .build();
+        testee.init(matcherConfig);
+
+        MailAddress sender = new MailAddress("[email protected]");
+        MailAddress recipient = new MailAddress("[email protected]");
+        Collection<MailAddress> matched = testee.match(FakeMail.builder()
+            .name("default-id")
+            .sender(sender)
+            .recipient(recipient)
+            .build());
+
+        assertThat(matched).isEmpty();
+    }
+
     @Test
     void shouldReturnSenderWhenHasAttribute() throws Exception {
         SenderHasLDAPAttribute testee = new 
SenderHasLDAPAttribute(LdapRepositoryConfiguration.from(ldapRepositoryConfigurationWithVirtualHosting(ldapContainer)));


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to