You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by bt...@apache.org on 2024/09/20 06:47:55 UTC

(james-project) 05/07: JAMES-3945 step 3: implementation of right management for subaddressing, with unit and integration tests

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

commit acf6a9b7755875acb55dcc31acf7e28da2a4fa5a
Author: Benoit TELLIER <bt...@linagora.com>
AuthorDate: Wed Sep 11 14:48:22 2024 +0200

    JAMES-3945 step 3: implementation of right management for subaddressing, with unit and integration tests
---
 .../org/apache/james/mailbox/model/MailboxACL.java |   4 +
 .../org/apache/james/modules/ACLProbeImpl.java     |   5 +
 .../org/apache/james/user/api/UsersRepository.java |   6 +-
 .../apache/james/mailets/SubAddressingTest.java    |  99 +++++++----
 .../james/transport/mailets/SubAddressing.java     |  77 ++++++--
 .../james/transport/mailets/SubAddressingTest.java | 197 +++++++++++++++++++--
 6 files changed, 330 insertions(+), 58 deletions(-)

diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxACL.java b/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxACL.java
index 8ce9ea2130..55ceabe09f 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxACL.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxACL.java
@@ -375,6 +375,10 @@ public class MailboxACL {
             return new EntryKey(name.asString(), NameType.user, false);
         }
 
+        public static EntryKey createNegativeUserEntryKey(Username name) {
+            return new EntryKey(name.asString(), NameType.user, true);
+        }
+
         public static EntryKey createUserEntryKey(Username name, boolean negative) {
             return new EntryKey(name.asString(), NameType.user, negative);
         }
diff --git a/server/container/guice/mailbox/src/main/java/org/apache/james/modules/ACLProbeImpl.java b/server/container/guice/mailbox/src/main/java/org/apache/james/modules/ACLProbeImpl.java
index 023ee16396..49e6ae77fa 100644
--- a/server/container/guice/mailbox/src/main/java/org/apache/james/modules/ACLProbeImpl.java
+++ b/server/container/guice/mailbox/src/main/java/org/apache/james/modules/ACLProbeImpl.java
@@ -60,6 +60,11 @@ public class ACLProbeImpl implements GuiceProbe, ACLProbe {
         mailboxManager.applyRightsCommand(mailboxPath, command, mailboxSession);
     }
 
+    public void executeCommand(MailboxPath mailboxPath, ACLCommand command) throws MailboxException {
+        MailboxSession mailboxSession = mailboxManager.createSystemSession(mailboxPath.getUser());
+        mailboxManager.applyRightsCommand(mailboxPath, command, mailboxSession);
+    }
+
     @Override
     public MailboxACL retrieveRights(MailboxPath mailboxPath) throws MailboxException {
         MailboxSession mailboxSession = mailboxManager.createSystemSession(mailboxPath.getUser());
diff --git a/server/data/data-api/src/main/java/org/apache/james/user/api/UsersRepository.java b/server/data/data-api/src/main/java/org/apache/james/user/api/UsersRepository.java
index 6d86163636..3edf2c303a 100644
--- a/server/data/data-api/src/main/java/org/apache/james/user/api/UsersRepository.java
+++ b/server/data/data-api/src/main/java/org/apache/james/user/api/UsersRepository.java
@@ -41,6 +41,8 @@ import reactor.core.publisher.Mono;
  */
 public interface UsersRepository {
 
+    String LOCALPART_DETAIL_DELIMITER = "+";
+
     /**
      * Adds a user to the repository with the specified password
      * 
@@ -149,9 +151,9 @@ public interface UsersRepository {
      */
     default Username getUsername(MailAddress mailAddress) throws UsersRepositoryException {
         if (supportVirtualHosting()) {
-            return Username.of(mailAddress.stripDetails("+").asString());
+            return Username.of(mailAddress.stripDetails(LOCALPART_DETAIL_DELIMITER).asString());
         } else {
-            return Username.of(mailAddress.stripDetails("+").getLocalPart());
+            return Username.of(mailAddress.stripDetails(LOCALPART_DETAIL_DELIMITER).getLocalPart());
         }
     }
 
diff --git a/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/SubAddressingTest.java b/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/SubAddressingTest.java
index b0eef88197..19b3581a28 100644
--- a/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/SubAddressingTest.java
+++ b/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/SubAddressingTest.java
@@ -29,12 +29,19 @@ import static org.apache.james.mailets.configuration.Constants.RECIPIENT;
 import static org.apache.james.mailets.configuration.Constants.awaitAtMostOneMinute;
 
 import java.io.File;
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.spec.InvalidKeySpecException;
 
+import org.apache.james.core.Username;
+import org.apache.james.mailbox.model.MailboxACL;
 import org.apache.james.mailbox.model.MailboxConstants;
+import org.apache.james.mailbox.model.MailboxPath;
 import org.apache.james.mailets.configuration.CommonProcessors;
 import org.apache.james.mailets.configuration.MailetConfiguration;
 import org.apache.james.mailets.configuration.ProcessorConfiguration;
-import org.apache.james.modules.MailboxProbeImpl;
+import org.apache.james.modules.ACLProbeImpl;
 import org.apache.james.modules.protocols.ImapGuiceProbe;
 import org.apache.james.modules.protocols.SmtpGuiceProbe;
 import org.apache.james.probe.DataProbe;
@@ -59,60 +66,90 @@ class SubAddressingTest {
 
     private TemporaryJamesServer jamesServer;
 
-    void setup() throws Exception {
+    void setup(File temporaryFolder) throws Exception {
+        jamesServer = TemporaryJamesServer.builder()
+            .withMailetContainer(TemporaryJamesServer.defaultMailetContainerConfiguration()
+                .postmaster(POSTMASTER)
+                .putProcessor(ProcessorConfiguration.transport()
+                    .addMailet(MailetConfiguration.builder()
+                        .matcher(All.class)
+                        .mailet(SubAddressing.class))
+                    .addMailetsFrom(CommonProcessors.transport())))
+            .build(temporaryFolder);
+        jamesServer.start();
+
         DataProbe dataProbe = jamesServer.getProbe(DataProbeImpl.class);
         dataProbe.addDomain(DEFAULT_DOMAIN);
         dataProbe.addUser(RECIPIENT, PASSWORD);
         dataProbe.addUser(FROM, PASSWORD);
-
-        jamesServer.getProbe(MailboxProbeImpl.class)
-            .createMailbox(MailboxConstants.USER_NAMESPACE, RECIPIENT, TARGETED_MAILBOX);
     }
 
     @AfterEach
-    void tearDown() {
+    void tearDown() throws IOException {
         if (jamesServer != null) {
             jamesServer.shutdown();
         }
+        testIMAPClient.close();
+        messageSender.close();
     }
 
     @Test
-    void subAddressedEmailShouldBeReceivedByDefault(@TempDir File temporaryFolder) throws Exception {
-        jamesServer = TemporaryJamesServer.builder().build(temporaryFolder);
-        jamesServer.start();
-        setup();
+    void subAddressedEmailShouldBeDeliveredInINBOXWhenSpecifiedFolderDoesNotExist(@TempDir File temporaryFolder) throws Exception {
+        setup(temporaryFolder);
 
-        messageSender.connect(LOCALHOST_IP, jamesServer.getProbe(SmtpGuiceProbe.class).getSmtpPort())
-            .authenticate(FROM, PASSWORD)
-            .sendMessage(FROM, "user2+detail-test@" + DEFAULT_DOMAIN);
+        //do not create mailbox
+
+        sendSubAddressedMail();
+        awaitSubAddressedMail(MailboxConstants.INBOX);
+    }
+
+    @Test
+    void subAddressedEmailShouldBeDeliveredInINBOXWhenNoRights(@TempDir File temporaryFolder) throws Exception {
+        setup(temporaryFolder);
 
+        // create mailbox
         testIMAPClient.connect(LOCALHOST_IP, jamesServer.getProbe(ImapGuiceProbe.class).getImapPort())
             .login(RECIPIENT, PASSWORD)
-            .select(MailboxConstants.INBOX)
-            .awaitMessage(awaitAtMostOneMinute);
+            .create(TARGETED_MAILBOX);
+
+        //do not give posting rights
+
+        sendSubAddressedMail();
+        awaitSubAddressedMail(MailboxConstants.INBOX);
     }
 
     @Test
-    void subAddressedEmailShouldBeDeliveredInSpecifiedFolder(@TempDir File temporaryFolder) throws Exception {
-        jamesServer = TemporaryJamesServer.builder()
-            .withMailetContainer(TemporaryJamesServer.defaultMailetContainerConfiguration()
-                .postmaster(POSTMASTER)
-                .putProcessor(ProcessorConfiguration.transport()
-                    .addMailet(MailetConfiguration.builder()
-                        .matcher(All.class)
-                        .mailet(SubAddressing.class))
-                    .addMailetsFrom(CommonProcessors.transport())))
-            .build(temporaryFolder);
-        jamesServer.start();
-        setup();
+    void subAddressedEmailShouldBeDeliveredInSpecifiedFolderWhenRights(@TempDir File temporaryFolder) throws Exception {
+        setup(temporaryFolder);
 
+        // create mailbox
+        testIMAPClient.connect(LOCALHOST_IP, jamesServer.getProbe(ImapGuiceProbe.class).getImapPort())
+            .login(RECIPIENT, PASSWORD)
+            .create(TARGETED_MAILBOX);
+
+        //give posting rights
+        jamesServer.getProbe(ACLProbeImpl.class).executeCommand(
+            MailboxPath.forUser(Username.of(RECIPIENT), TARGETED_MAILBOX),
+            MailboxACL.command()
+                .key(MailboxACL.ANYBODY_KEY)
+                .rights(MailboxACL.Right.Post)
+                .asAddition());
+
+        sendSubAddressedMail();
+        awaitSubAddressedMail(TARGETED_MAILBOX);
+    }
+
+    private void sendSubAddressedMail() throws IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidKeySpecException {
         messageSender.connect(LOCALHOST_IP, jamesServer.getProbe(SmtpGuiceProbe.class).getSmtpPort())
             .authenticate(FROM, PASSWORD)
-            .sendMessage(FROM, "user2+any@" + DEFAULT_DOMAIN);
+            .sendMessage(FROM, "user2+" + TARGETED_MAILBOX + "@" + DEFAULT_DOMAIN);
+    }
 
-        testIMAPClient.connect(LOCALHOST_IP, jamesServer.getProbe(ImapGuiceProbe.class).getImapPort())
+    private void awaitSubAddressedMail(String expectedMailbox) throws IOException {
+        testIMAPClient
+            .connect(LOCALHOST_IP, jamesServer.getProbe(ImapGuiceProbe.class).getImapPort())
             .login(RECIPIENT, PASSWORD)
-            .select("any")
+            .select(expectedMailbox)
             .awaitMessage(awaitAtMostOneMinute);
     }
-}
+}
\ No newline at end of file
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/SubAddressing.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/SubAddressing.java
index 57587c7cb7..73edff3121 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/SubAddressing.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/SubAddressing.java
@@ -19,33 +19,90 @@
 
 package org.apache.james.transport.mailets;
 
-import com.github.fge.lambdas.Throwing;
-import com.google.common.collect.ImmutableList;
+import static org.apache.james.mailbox.MessageManager.MailboxMetaData.RecentMode.IGNORE;
+
 import jakarta.inject.Inject;
+import jakarta.inject.Named;
 import jakarta.mail.MessagingException;
+
 import org.apache.james.core.MailAddress;
+import org.apache.james.core.MaybeSender;
+import org.apache.james.core.Username;
+import org.apache.james.mailbox.MailboxManager;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.MessageManager;
+import org.apache.james.mailbox.acl.MailboxACLResolver;
+import org.apache.james.mailbox.acl.UnionMailboxACLResolver;
+import org.apache.james.mailbox.exception.MailboxException;
+import org.apache.james.mailbox.exception.MailboxNotFoundException;
+import org.apache.james.mailbox.exception.UnsupportedRightException;
+import org.apache.james.mailbox.model.MailboxACL;
+import org.apache.james.mailbox.model.MailboxPath;
 import org.apache.james.user.api.UsersRepository;
+import org.apache.james.user.api.UsersRepositoryException;
 import org.apache.mailet.Mail;
 import org.apache.mailet.StorageDirective;
 import org.apache.mailet.base.GenericMailet;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.fge.lambdas.Throwing;
+import com.google.common.collect.ImmutableList;
+
 
 public class SubAddressing extends GenericMailet {
+    private static final Logger LOG = LoggerFactory.getLogger(SubAddressing.class);
+    private static final Username NO_ASSOCIATED_USER = null;
+
     private final UsersRepository usersRepository;
+    private final MailboxManager mailboxManager;
 
     @Inject
-    public SubAddressing(UsersRepository usersRepository) {
+    public SubAddressing(UsersRepository usersRepository, @Named("mailboxmanager") MailboxManager mailboxManager) {
         this.usersRepository = usersRepository;
+        this.mailboxManager = mailboxManager;
     }
 
     @Override
     public void service(Mail mail) throws MessagingException {
         mail.getRecipients().forEach(recipient ->
-            recipient.getLocalPartDetails("+")
-                .ifPresent(
-                    Throwing.consumer(details ->
-                    StorageDirective.builder().targetFolders(ImmutableList.of(details)).build()
-                        .encodeAsAttributes(usersRepository.getUsername(recipient))
-                        .forEach(mail::setAttribute))
-                ));
+            recipient.getLocalPartDetails(UsersRepository.LOCALPART_DETAIL_DELIMITER)
+                .ifPresent(Throwing.consumer(targetFolder -> postIfHasRight(mail, recipient, targetFolder))));
+    }
+
+    private void postIfHasRight(Mail mail, MailAddress recipient, String targetFolder) throws UsersRepositoryException, MailboxException {
+        if (hasPostRight(mail, recipient, targetFolder)) {
+            StorageDirective.builder().targetFolders(ImmutableList.of(targetFolder)).build()
+                .encodeAsAttributes(usersRepository.getUsername(recipient))
+                .forEach(mail::setAttribute);
+        } else {
+            LOG.info("{} tried to address {}'s subfolder `{}` but they did not have the right to",
+                mail.getMaybeSender().toString(), recipient.stripDetails(UsersRepository.LOCALPART_DETAIL_DELIMITER), targetFolder);
+        }
+    }
+
+    private Boolean hasPostRight(Mail mail, MailAddress recipient, String targetFolder) throws MailboxException, UsersRepositoryException {
+        try {
+            return resolvePostRight(retrieveMailboxACL(recipient, targetFolder), mail.getMaybeSender(), recipient);
+        } catch (MailboxNotFoundException e) {
+            LOG.info("{}'s subfolder `{}` was tried to be addressed but it does not exist", recipient.stripDetails(UsersRepository.LOCALPART_DETAIL_DELIMITER), targetFolder);
+            return false;
+        }
+    }
+
+    private MailboxACL retrieveMailboxACL(MailAddress recipient, String targetFolder) throws MailboxException, UsersRepositoryException {
+        Username recipientUsername = usersRepository.getUsername(recipient);
+        MailboxSession session = mailboxManager.createSystemSession(recipientUsername);
+        return mailboxManager.getMailbox(MailboxPath.forUser(recipientUsername, targetFolder), session)
+            .getMetaData(IGNORE, session, MessageManager.MailboxMetaData.FetchGroup.NO_COUNT)
+            .getACL();
+    }
+
+    private boolean resolvePostRight(MailboxACL acl, MaybeSender maybeSender, MailAddress recipient) throws UnsupportedRightException {
+        return new UnionMailboxACLResolver().resolveRights(
+                maybeSender.asOptional()
+                    .map(Throwing.function(usersRepository::getUsername))
+                    .orElse(NO_ASSOCIATED_USER), acl, recipient.asString())
+            .contains(MailboxACL.Right.Post);
     }
 }
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/SubAddressingTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/SubAddressingTest.java
index 014a2ef4b8..8fe4055cb1 100644
--- a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/SubAddressingTest.java
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/SubAddressingTest.java
@@ -19,39 +19,206 @@
 
 package org.apache.james.transport.mailets;
 
+import static org.apache.james.transport.mailets.WithStorageDirectiveTest.NO_DOMAIN_LIST;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.Collection;
+
+import jakarta.mail.MessagingException;
+
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.james.core.MailAddress;
+import org.apache.james.core.Username;
+import org.apache.james.mailbox.MailboxManager;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.exception.MailboxException;
+import org.apache.james.mailbox.inmemory.manager.InMemoryIntegrationResources;
+import org.apache.james.mailbox.model.MailboxACL;
+import org.apache.james.mailbox.model.MailboxId;
+import org.apache.james.mailbox.model.MailboxPath;
+import org.apache.james.user.api.UsersRepository;
+import org.apache.james.user.api.UsersRepositoryException;
 import org.apache.james.user.memory.MemoryUsersRepository;
 import org.apache.mailet.Attribute;
 import org.apache.mailet.AttributeName;
 import org.apache.mailet.AttributeValue;
-import org.apache.mailet.base.MailAddressFixture;
+import org.apache.mailet.Mail;
 import org.apache.mailet.base.test.FakeMail;
 import org.apache.mailet.base.test.FakeMailetConfig;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
-import java.util.Collection;
-
-import static org.apache.james.transport.mailets.WithStorageDirectiveTest.NO_DOMAIN_LIST;
-import static org.assertj.core.api.Assertions.assertThat;
 
 public class SubAddressingTest {
+
+    private static final String SENDER1 = "sender1@localhost";
+    private static final String SENDER2 = "sender2@localhost";
+    private static final String RECIPIENT = "recipient@localhost";
+    private static final String TARGET = "targetfolder";
+    private static final String UNEXISTING_TARGET = "unexistingfolder";
+
+    private MailboxManager mailboxManager;
+    private SubAddressing testee;
+    private UsersRepository usersRepository;
+    private Username sender1Username;
+    private Username recipientUsername;
+    private MailboxSession recipientSession;
+    private MailboxId targetMailboxId;
+
+    @BeforeEach
+    void setUp() throws MailboxException, MessagingException, UsersRepositoryException {
+        mailboxManager = InMemoryIntegrationResources.defaultResources().getMailboxManager();
+        usersRepository = MemoryUsersRepository.withVirtualHosting(NO_DOMAIN_LIST);
+
+        recipientUsername = usersRepository.getUsername(new MailAddress(RECIPIENT));
+        sender1Username = usersRepository.getUsername(new MailAddress(SENDER1));
+
+        recipientSession = mailboxManager.createSystemSession(recipientUsername);
+        targetMailboxId = mailboxManager.createMailbox(
+            MailboxPath.forUser(recipientUsername, TARGET), recipientSession).get();
+
+        testee = new SubAddressing(usersRepository, mailboxManager);
+        testee.init(FakeMailetConfig.builder().build());
+    }
+
     @Test
-    void shouldAddStorageDirectiveMatchingDetails() throws Exception {
-        SubAddressing testee = new SubAddressing(MemoryUsersRepository.withVirtualHosting(NO_DOMAIN_LIST));
-        testee.init(FakeMailetConfig.builder()
-            .build());
+    void shouldNotAddStorageDirectiveWhenTargetMailboxDoesNotExist() throws Exception {
+        Mail mail = mailBuilder(UNEXISTING_TARGET).sender(SENDER1).build();
+        testee.service(mail);
 
-        FakeMail mail = FakeMail.builder()
-            .name("name")
-            .recipient("recipient1+any@localhost")
-            .build();
+        AttributeName recipient = AttributeName.of("DeliveryPaths_recipient@localhost");
+        assertThat(mail.attributes().map(this::unbox))
+            .doesNotContain(Pair.of(recipient, UNEXISTING_TARGET));
+    }
+
+    @Test
+    void shouldNotAddStorageDirectiveWhenNobodyHasRight() throws Exception {
+        removePostRightForKey(MailboxACL.ANYBODY_KEY);
+
+        Mail mail = mailBuilder(TARGET).sender(SENDER1).build();
+        testee.service(mail);
+
+        AttributeName recipient = AttributeName.of("DeliveryPaths_recipient@localhost");
+        assertThat(mail.attributes().map(this::unbox))
+            .doesNotContain(Pair.of(recipient, TARGET));
+    }
+
+
+    @Test
+    void shouldAddStorageDirectiveWhenAnybodyHasRight() throws Exception {
+        givePostRightForKey(MailboxACL.ANYBODY_KEY);
 
+        Mail mail = mailBuilder(TARGET).sender(SENDER1).build();
         testee.service(mail);
 
-        AttributeName recipient1 = AttributeName.of("DeliveryPaths_recipient1@localhost");
+        AttributeName recipient = AttributeName.of("DeliveryPaths_recipient@localhost");
         assertThat(mail.attributes().map(this::unbox))
-            .containsOnly(Pair.of(recipient1, "any"));
+            .containsOnly(Pair.of(recipient, TARGET));
+    }
+
+    //@Disabled
+    @Test
+    void shouldAddStorageDirectiveWhenSenderIsWhiteListed() throws Exception {
+        // whitelist sender 1 and send from sender 1
+        removePostRightForKey(MailboxACL.ANYBODY_KEY);
+        givePostRightForKey(MailboxACL.EntryKey.createUserEntryKey(sender1Username));
+
+        Mail mail = mailBuilder(TARGET).sender(SENDER1).build();
+        testee.service(mail);
+
+        AttributeName recipient = AttributeName.of("DeliveryPaths_recipient@localhost");
+        assertThat(mail.attributes().map(this::unbox))
+            .containsOnly(Pair.of(recipient, TARGET));
+    }
+
+    @Test
+    void shouldNotAddStorageDirectiveWhenSenderIsNotWhiteListed() throws Exception {
+        // whitelist sender 1 and send from sender 2
+        removePostRightForKey(MailboxACL.ANYBODY_KEY);
+        givePostRightForKey(MailboxACL.EntryKey.createUserEntryKey(sender1Username));
+
+        Mail mail = mailBuilder(TARGET).sender(SENDER2).build();
+        testee.service(mail);
+
+        AttributeName recipient = AttributeName.of("DeliveryPaths_recipient@localhost");
+        assertThat(mail.attributes().map(this::unbox))
+            .doesNotContain(Pair.of(recipient, TARGET));
+    }
+
+    @Test
+    void shouldNotAddStorageDirectiveWhenSenderIsBlackListed() throws Exception {
+        // blacklist sender 1 and send from sender 1
+        givePostRightForKey(MailboxACL.ANYBODY_KEY);
+        givePostRightForKey(MailboxACL.EntryKey.createNegativeUserEntryKey(sender1Username));
+
+        Mail mail = mailBuilder(TARGET).sender(SENDER1).build();
+        testee.service(mail);
+
+        AttributeName recipient = AttributeName.of("DeliveryPaths_recipient@localhost");
+        assertThat(mail.attributes().map(this::unbox))
+            .doesNotContain(Pair.of(recipient, TARGET));
+    }
+
+    @Test
+    void shouldAddStorageDirectiveWhenSenderIsNotBlackListed() throws Exception {
+        // blacklist sender 1 and send from sender 2
+        givePostRightForKey(MailboxACL.ANYBODY_KEY);
+        removePostRightForKey(MailboxACL.EntryKey.createUserEntryKey(sender1Username));
+
+        Mail mail = mailBuilder(TARGET).sender(SENDER2).build();
+        testee.service(mail);
+
+        AttributeName recipient = AttributeName.of("DeliveryPaths_recipient@localhost");
+        assertThat(mail.attributes().map(this::unbox))
+            .containsOnly(Pair.of(recipient, TARGET));
+    }
+
+    @Test
+    void shouldAddStorageDirectiveWhenAnybodyHasRightAndSenderIsUnknown() throws Exception {
+        givePostRightForKey(MailboxACL.ANYBODY_KEY);
+
+        Mail mail = mailBuilder(TARGET).build();
+        testee.service(mail);
+
+        AttributeName recipient = AttributeName.of("DeliveryPaths_recipient@localhost");
+        assertThat(mail.attributes().map(this::unbox))
+            .containsOnly(Pair.of(recipient, TARGET));
+    }
+
+    @Test
+    void shouldNotAddStorageDirectiveWhenNobodyHasRightAndSenderIsUnknown() throws Exception {
+        removePostRightForKey(MailboxACL.ANYBODY_KEY);
+
+        Mail mail = mailBuilder(TARGET).build();
+        testee.service(mail);
+
+        AttributeName recipient = AttributeName.of("DeliveryPaths_recipient@localhost");
+        assertThat(mail.attributes().map(this::unbox))
+            .doesNotContain(Pair.of(recipient, TARGET));
+    }
+
+    private FakeMail.Builder mailBuilder(String targetFolder) throws MessagingException {
+        return FakeMail.builder()
+            .name("name")
+            .recipient("recipient+" + targetFolder + "@localhost");
+    }
+
+    private void givePostRightForKey(MailboxACL.EntryKey key) throws MailboxException {
+        MailboxACL.ACLCommand command = MailboxACL.command()
+            .key(key)
+            .rights(MailboxACL.Right.Post)
+            .asAddition();
+
+        mailboxManager.applyRightsCommand(targetMailboxId, command, recipientSession);
+    }
+
+    private void removePostRightForKey(MailboxACL.EntryKey key) throws MailboxException {
+        MailboxACL.ACLCommand command = MailboxACL.command()
+            .key(key)
+            .rights(MailboxACL.Right.Post)
+            .asRemoval();
+
+        mailboxManager.applyRightsCommand(targetMailboxId, command, recipientSession);
     }
 
     Pair<AttributeName, String> unbox(Attribute attribute) {


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