JAMES-2188 A user can't set a message in a mailbox from someone else.

Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/bb1eeec8
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/bb1eeec8
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/bb1eeec8

Branch: refs/heads/master
Commit: bb1eeec860dfadc0663939836c662f2f2865e598
Parents: 6ff67ca
Author: Antoine Duprat <adup...@linagora.com>
Authored: Mon Oct 16 14:46:40 2017 +0200
Committer: benwa <btell...@linagora.com>
Committed: Wed Oct 18 09:01:56 2017 +0700

----------------------------------------------------------------------
 .../jmap/methods/MailboxRightsException.java    | 29 +++++++
 .../methods/SetMessagesCreationProcessor.java   | 41 ++++++++-
 .../SetMessagesCreationProcessorTest.java       | 87 +++++++++++++++++++-
 3 files changed, 151 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/bb1eeec8/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MailboxRightsException.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MailboxRightsException.java
 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MailboxRightsException.java
new file mode 100644
index 0000000..f47e7d0
--- /dev/null
+++ 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MailboxRightsException.java
@@ -0,0 +1,29 @@
+/****************************************************************
+ * 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.jmap.methods;
+
+import org.apache.james.mailbox.exception.MailboxException;
+
+public class MailboxRightsException extends MailboxException {
+
+    public MailboxRightsException() {
+        super();
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/bb1eeec8/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesCreationProcessor.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesCreationProcessor.java
 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesCreationProcessor.java
index 3087e71..8c482e3 100644
--- 
a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesCreationProcessor.java
+++ 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesCreationProcessor.java
@@ -57,6 +57,7 @@ import org.apache.james.jmap.send.MailSpool;
 import org.apache.james.jmap.utils.SystemMailboxesProvider;
 import org.apache.james.lifecycle.api.LifecycleUtil;
 import org.apache.james.mailbox.AttachmentManager;
+import org.apache.james.mailbox.MailboxManager;
 import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.MessageManager;
 import org.apache.james.mailbox.exception.AttachmentNotFoundException;
@@ -65,6 +66,7 @@ import 
org.apache.james.mailbox.exception.MailboxNotFoundException;
 import org.apache.james.mailbox.model.AttachmentId;
 import org.apache.james.mailbox.model.Cid;
 import org.apache.james.mailbox.model.ComposedMessageId;
+import org.apache.james.mailbox.model.MailboxId;
 import org.apache.james.mailbox.model.MessageAttachment;
 import org.apache.james.metrics.api.MetricFactory;
 import org.apache.james.metrics.api.TimeMetric;
@@ -93,6 +95,8 @@ public class SetMessagesCreationProcessor implements 
SetMessagesProcessor {
     private final SystemMailboxesProvider systemMailboxesProvider;
     private final AttachmentManager attachmentManager;
     private final MetricFactory metricFactory;
+    private final MailboxManager mailboxManager;
+    private final MailboxId.Factory mailboxIdFactory;
     
     @VisibleForTesting @Inject
     SetMessagesCreationProcessor(MIMEMessageConverter mimeMessageConverter,
@@ -100,7 +104,10 @@ public class SetMessagesCreationProcessor implements 
SetMessagesProcessor {
                                  MailFactory mailFactory,
                                  MessageFactory messageFactory,
                                  SystemMailboxesProvider 
systemMailboxesProvider,
-                                 AttachmentManager attachmentManager, 
MetricFactory metricFactory) {
+                                 AttachmentManager attachmentManager, 
+                                 MetricFactory metricFactory,
+                                 MailboxManager mailboxManager,
+                                 MailboxId.Factory mailboxIdFactory) {
         this.mimeMessageConverter = mimeMessageConverter;
         this.mailSpool = mailSpool;
         this.mailFactory = mailFactory;
@@ -108,6 +115,8 @@ public class SetMessagesCreationProcessor implements 
SetMessagesProcessor {
         this.systemMailboxesProvider = systemMailboxesProvider;
         this.attachmentManager = attachmentManager;
         this.metricFactory = metricFactory;
+        this.mailboxManager = mailboxManager;
+        this.mailboxIdFactory = mailboxIdFactory;
     }
 
     @Override
@@ -167,6 +176,14 @@ public class SetMessagesCreationProcessor implements 
SetMessagesProcessor {
                         .description(e.getMessage())
                         .build());
 
+        } catch (MailboxRightsException e) {
+            LOG.error("Appending message in an unknown mailbox", e);
+            responseBuilder.notCreated(create.getCreationId(), 
+                    SetError.builder()
+                        .type("error")
+                        .description("MailboxId invalid")
+                        .build());
+
         } catch (MailboxException | MessagingException e) {
             LOG.error("Unexpected error while creating message", e);
             responseBuilder.notCreated(create.getCreationId(), 
@@ -223,7 +240,12 @@ public class SetMessagesCreationProcessor implements 
SetMessagesProcessor {
         return AttachmentId.from(attachment.getBlobId().getRawValue());
     }
 
-    private void validateRights(CreationMessageEntry entry, MailboxSession 
session) throws MailboxSendingNotAllowedException {
+    private void validateRights(CreationMessageEntry entry, MailboxSession 
session) throws MailboxSendingNotAllowedException, MailboxRightsException {
+        validateUserIsInSenders(entry, session);
+        validateIsUserOwnerOfMailboxes(entry, session);
+    }
+
+    private void validateUserIsInSenders(CreationMessageEntry entry, 
MailboxSession session) throws MailboxSendingNotAllowedException {
         List<String> allowedSenders = 
ImmutableList.of(session.getUser().getUserName());
         if (!isAllowedFromAddress(entry.getValue(), allowedSenders)) {
             throw new MailboxSendingNotAllowedException(allowedSenders);
@@ -238,7 +260,20 @@ public class SetMessagesCreationProcessor implements 
SetMessagesProcessor {
                 .orElse(false);
     }
 
-    
+    @VisibleForTesting void 
validateIsUserOwnerOfMailboxes(CreationMessageEntry entry, MailboxSession 
session) throws MailboxRightsException {
+        if (containsMailboxNotOwn(entry.getValue().getMailboxIds(), session)) {
+            throw new MailboxRightsException();
+        }
+    }
+
+    private boolean containsMailboxNotOwn(List<String> mailboxIds, 
MailboxSession session) {
+        return mailboxIds.stream()
+            .map(mailboxIdFactory::fromString)
+            .map(Throwing.function(mailboxId -> 
mailboxManager.getMailbox(mailboxId, session)))
+            .map(Throwing.function(MessageManager::getMailboxPath))
+            .anyMatch(path -> !session.getUser().isSameUser(path.getUser()));
+    }
+
     private MessageWithId handleOutboxMessages(CreationMessageEntry entry, 
MailboxSession session) throws MailboxException, MessagingException {
         MessageManager outbox = getMailboxWithRole(session, 
Role.OUTBOX).orElseThrow(() -> new 
MailboxNotFoundException(Role.OUTBOX.serialize()));
         if (!isRequestForSending(entry.getValue(), session)) {

http://git-wip-us.apache.org/repos/asf/james-project/blob/bb1eeec8/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesCreationProcessorTest.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesCreationProcessorTest.java
 
b/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesCreationProcessorTest.java
index c1b9f00..8618b11 100644
--- 
a/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesCreationProcessorTest.java
+++ 
b/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesCreationProcessorTest.java
@@ -56,15 +56,19 @@ import org.apache.james.jmap.utils.HtmlTextExtractor;
 import org.apache.james.jmap.utils.SystemMailboxesProvider;
 import org.apache.james.mailbox.AttachmentManager;
 import org.apache.james.mailbox.BlobManager;
+import org.apache.james.mailbox.MailboxManager;
 import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.MessageManager;
 import org.apache.james.mailbox.MessageUid;
 import org.apache.james.mailbox.exception.AttachmentNotFoundException;
 import org.apache.james.mailbox.exception.MailboxException;
+import org.apache.james.mailbox.exception.MailboxNotFoundException;
 import org.apache.james.mailbox.inmemory.InMemoryId;
 import org.apache.james.mailbox.mock.MockMailboxSession;
 import org.apache.james.mailbox.model.AttachmentId;
 import org.apache.james.mailbox.model.ComposedMessageId;
+import org.apache.james.mailbox.model.MailboxId;
+import org.apache.james.mailbox.model.MailboxId.Factory;
 import org.apache.james.mailbox.model.MailboxPath;
 import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.model.TestMessageId;
@@ -113,6 +117,8 @@ public class SetMessagesCreationProcessorTest {
     private MockMailboxSession session;
     private MIMEMessageConverter mimeMessageConverter;
     private AttachmentManager mockedAttachmentManager;
+    private MailboxManager mockedMailboxManager;
+    private Factory mockedMailboxIdFactory;
     private SetMessagesCreationProcessor sut;
     private MessageManager outbox;
     private MessageManager drafts;
@@ -121,6 +127,7 @@ public class SetMessagesCreationProcessorTest {
 
     @Rule
     public ExpectedException expectedException = ExpectedException.none();
+
     @Before
     public void setUp() throws MailboxException {
         HtmlTextExtractor htmlTextExtractor = mock(HtmlTextExtractor.class);
@@ -133,13 +140,20 @@ public class SetMessagesCreationProcessorTest {
         mockedMailSpool = mock(MailSpool.class);
         mockedMailFactory = mock(MailFactory.class);
         mockedAttachmentManager = mock(AttachmentManager.class);
+        mockedMailboxManager = mock(MailboxManager.class);
+        mockedMailboxIdFactory = mock(MailboxId.Factory.class);
         
         fakeSystemMailboxesProvider = new TestSystemMailboxesProvider(() -> 
optionalOutbox, () -> optionalDrafts);
         session = new MockMailboxSession(USER);
         mimeMessageConverter = new MIMEMessageConverter();
-        sut = new SetMessagesCreationProcessor(mimeMessageConverter, 
mockedMailSpool, mockedMailFactory, messageFactory, 
fakeSystemMailboxesProvider, mockedAttachmentManager, new NoopMetricFactory());
+        sut = new SetMessagesCreationProcessor(mimeMessageConverter, 
mockedMailSpool, mockedMailFactory, messageFactory, 
fakeSystemMailboxesProvider, mockedAttachmentManager, new NoopMetricFactory(), 
mockedMailboxManager, mockedMailboxIdFactory);
         
         outbox = mock(MessageManager.class);
+        when(mockedMailboxIdFactory.fromString(OUTBOX_ID.serialize()))
+            .thenReturn(OUTBOX_ID);
+        when(mockedMailboxManager.getMailbox(OUTBOX_ID, session))
+            .thenReturn(outbox);
+        
         when(outbox.getId()).thenReturn(OUTBOX_ID);
         when(outbox.getMailboxPath()).thenReturn(MailboxPath.forUser(USER, 
OUTBOX));
         
@@ -216,7 +230,7 @@ public class SetMessagesCreationProcessorTest {
     @Test
     public void 
processShouldReturnNonEmptyCreatedWhenRequestHasNonEmptyCreate() throws 
MailboxException {
         // Given
-        sut = new SetMessagesCreationProcessor(mimeMessageConverter, 
mockedMailSpool, mockedMailFactory, messageFactory, 
fakeSystemMailboxesProvider, mockedAttachmentManager, new NoopMetricFactory());
+        sut = new SetMessagesCreationProcessor(mimeMessageConverter, 
mockedMailSpool, mockedMailFactory, messageFactory, 
fakeSystemMailboxesProvider, mockedAttachmentManager, new NoopMetricFactory(), 
mockedMailboxManager, mockedMailboxIdFactory);
 
         // When
         SetMessagesResponse result = sut.process(createMessageInOutbox, 
session);
@@ -231,7 +245,7 @@ public class SetMessagesCreationProcessorTest {
     public void processShouldReturnErrorWhenOutboxNotFound() {
         // Given
         TestSystemMailboxesProvider doNotProvideOutbox = new 
TestSystemMailboxesProvider(Optional::empty, () -> optionalDrafts);
-        SetMessagesCreationProcessor sut = new 
SetMessagesCreationProcessor(mimeMessageConverter, mockedMailSpool, 
mockedMailFactory, messageFactory, doNotProvideOutbox, mockedAttachmentManager, 
new NoopMetricFactory());
+        SetMessagesCreationProcessor sut = new 
SetMessagesCreationProcessor(mimeMessageConverter, mockedMailSpool, 
mockedMailFactory, messageFactory, doNotProvideOutbox, mockedAttachmentManager, 
new NoopMetricFactory(), mockedMailboxManager, mockedMailboxIdFactory);
         // When
         SetMessagesResponse actual = sut.process(createMessageInOutbox, 
session);
         
@@ -347,6 +361,73 @@ public class SetMessagesCreationProcessorTest {
             .isInstanceOf(AttachmentsNotFoundException.class)
             .matches(e -> 
((AttachmentsNotFoundException)e).getAttachmentIds().containsAll(ImmutableSet.of(unknownBlobId1,
 unknownBlobId2)));
     }
+
+    @Test
+    public void 
validateIsUserOwnerOfMailboxesShouldThrowWhenMailboxIdDoesntExist() throws 
Exception {
+        InMemoryId mailboxId = InMemoryId.of(6789);
+        when(mockedMailboxManager.getMailbox(mailboxId, session))
+            .thenThrow(new MailboxNotFoundException(mailboxId));
+        when(mockedMailboxIdFactory.fromString(mailboxId.serialize()))
+            .thenReturn(mailboxId);
+        CreationMessageId creationMessageId = 
CreationMessageId.of("anything-really");
+        CreationMessageEntry entry = new 
CreationMessageEntry(creationMessageId, 
creationMessageBuilder.mailboxId(mailboxId.serialize()).build());
+
+        assertThatThrownBy(() -> sut.validateIsUserOwnerOfMailboxes(entry, 
session));
+    }
+
+    @Test
+    public void 
validateIsUserOwnerOfMailboxesShouldThrowWhenRetrievingMailboxPathFails() 
throws Exception {
+        CreationMessageId creationMessageId = 
CreationMessageId.of("anything-really");
+        InMemoryId mailboxId = InMemoryId.of(6789);
+        MessageManager mailbox = mock(MessageManager.class);
+        when(mockedMailboxManager.getMailbox(mailboxId, session))
+            .thenReturn(mailbox);
+        when(mockedMailboxIdFactory.fromString(mailboxId.serialize()))
+            .thenReturn(mailboxId);
+        when(mailbox.getMailboxPath())
+            .thenThrow(new MailboxException());
+
+        CreationMessageEntry entry = new 
CreationMessageEntry(creationMessageId, 
creationMessageBuilder.mailboxId(mailboxId.serialize()).build());
+
+        assertThatThrownBy(() -> sut.validateIsUserOwnerOfMailboxes(entry, 
session));
+    }
+
+    @Test
+    public void 
validateIsUserOwnerOfMailboxesShouldThrowWhenUserIsNotTheOwnerOfTheMailbox() 
throws Exception {
+        CreationMessageId creationMessageId = 
CreationMessageId.of("anything-really");
+        InMemoryId mailboxId = InMemoryId.of(6789);
+        MessageManager mailbox = mock(MessageManager.class);
+        when(mockedMailboxManager.getMailbox(mailboxId, session))
+            .thenReturn(mailbox);
+        when(mockedMailboxIdFactory.fromString(mailboxId.serialize()))
+            .thenReturn(mailboxId);
+        when(mailbox.getMailboxPath())
+            .thenReturn(MailboxPath.forUser("otheru...@example.com", 
mailboxId.serialize()));
+
+
+        CreationMessageEntry entry = new 
CreationMessageEntry(creationMessageId, 
creationMessageBuilder.mailboxId(mailboxId.serialize()).build());
+
+        assertThatThrownBy(() -> sut.validateIsUserOwnerOfMailboxes(entry, 
session))
+            .isInstanceOf(MailboxRightsException.class);
+    }
+
+    @Test
+    public void 
validateIsUserOwnerOfMailboxesShouldNotThrowWhenUserIsTheOwnerOfTheMailbox() 
throws Exception {
+        CreationMessageId creationMessageId = 
CreationMessageId.of("anything-really");
+        InMemoryId mailboxId = InMemoryId.of(6789);
+        MessageManager mailbox = mock(MessageManager.class);
+        when(mockedMailboxManager.getMailbox(mailboxId, session))
+            .thenReturn(mailbox);
+        when(mockedMailboxIdFactory.fromString(mailboxId.serialize()))
+            .thenReturn(mailboxId);
+        when(mailbox.getMailboxPath())
+            .thenReturn(MailboxPath.forUser(USER, mailboxId.serialize()));
+
+
+        CreationMessageEntry entry = new 
CreationMessageEntry(creationMessageId, 
creationMessageBuilder.mailboxId(mailboxId.serialize()).build());
+
+        sut.validateIsUserOwnerOfMailboxes(entry, session);
+    }
     
     public static class TestSystemMailboxesProvider implements 
SystemMailboxesProvider {
 


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

Reply via email to