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


The following commit(s) were added to refs/heads/master by this push:
     new 4c4d6d3df8 James-4097 Allow disabling same-domain requirement when 
assigning rights (#2573)
4c4d6d3df8 is described below

commit 4c4d6d3df8f47e7c598e4ef86764bab0f9effccd
Author: j-hellenberg <47941549+j-hellenb...@users.noreply.github.com>
AuthorDate: Mon Dec 23 23:13:55 2024 +0100

    James-4097 Allow disabling same-domain requirement when assigning rights 
(#2573)
---
 docs/modules/servers/partials/configure/jvm.adoc   | 15 +++++-
 .../james/mailbox/store/StoreRightManager.java     | 21 +++++---
 .../james/mailbox/store/StoreRightManagerTest.java | 57 ++++++++++++++++++++--
 .../docker-configuration/jvm.properties            |  5 +-
 .../sample-configuration/jvm.properties            |  5 +-
 .../docker-configuration/jvm.properties            |  5 +-
 .../sample-configuration/jvm.properties            |  5 +-
 .../docker-configuration/jvm.properties            |  5 +-
 .../sample-configuration/jvm.properties            |  5 +-
 .../jpa-app/sample-configuration/jvm.properties    |  5 +-
 .../memory-app/sample-configuration/jvm.properties |  5 +-
 11 files changed, 113 insertions(+), 20 deletions(-)

diff --git a/docs/modules/servers/partials/configure/jvm.adoc 
b/docs/modules/servers/partials/configure/jvm.adoc
index 15e409047b..fdc71ef205 100644
--- a/docs/modules/servers/partials/configure/jvm.adoc
+++ b/docs/modules/servers/partials/configure/jvm.adoc
@@ -172,4 +172,17 @@ james.deduplicating.blobstore.thread.switch.threshold=32768
 
 # Count of octet from which streams are buffered to files and not to memory
 james.deduplicating.blobstore.file.threshold=10240
-----
\ No newline at end of file
+----
+
+== Allow users to have rights for shares of different domain
+
+Typically, preventing users to obtain rights for shares of another domain is a 
useful security layer.
+However, in multi-tenancy deployments, this can be useful (for example, 
students might be given access to a shared mailbox
+residing under the @university.edu domain with their @student.university.edu 
address).
+
+Optional. Boolean. Defaults to false.
+
+Ex in `jvm.properties`
+----
+james.rights.crossdomain.allow=false
+----
diff --git 
a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreRightManager.java
 
b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreRightManager.java
index 1a22ae328e..49ad62fe69 100644
--- 
a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreRightManager.java
+++ 
b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreRightManager.java
@@ -61,6 +61,9 @@ import com.google.common.collect.ImmutableMap;
 import reactor.core.publisher.Mono;
 
 public class StoreRightManager implements RightManager {
+    @VisibleForTesting
+    static Boolean IS_CROSS_DOMAIN_ACCESS_ALLOWED = 
Boolean.parseBoolean(System.getProperty("james.rights.crossdomain.allow", 
"false"));
+
     private final EventBus eventBus;
     private final MailboxSessionMapperFactory mailboxSessionMapperFactory;
     private final MailboxACLResolver aclResolver;
@@ -185,7 +188,7 @@ public class StoreRightManager implements RightManager {
     @Override
     public Mono<Void> applyRightsCommandReactive(MailboxPath mailboxPath, 
ACLCommand mailboxACLCommand, MailboxSession session) {
         return Mono.just(mailboxSessionMapperFactory.getMailboxMapper(session))
-            .doOnNext(Throwing.consumer(mapper -> 
assertSharesBelongsToUserDomain(mailboxPath.getUser(), mailboxACLCommand)))
+            .doOnNext(Throwing.consumer(mapper -> 
assertUserHasAccessToShareeDomains(mailboxPath.getUser(), mailboxACLCommand)))
             .flatMap(mapper -> mapper.findMailboxByPath(mailboxPath)
                 .doOnNext(Throwing.consumer(mailbox -> 
assertHaveAccessTo(mailbox, session)))
                 .flatMap(mailbox -> mapper.updateACL(mailbox, 
mailboxACLCommand)
@@ -211,8 +214,8 @@ public class StoreRightManager implements RightManager {
         applyRightsCommand(mailbox.generateAssociatedPath(), 
mailboxACLCommand, session);
     }
 
-    private void assertSharesBelongsToUserDomain(Username user, ACLCommand 
mailboxACLCommand) throws DifferentDomainException {
-        assertSharesBelongsToUserDomain(user, 
ImmutableMap.of(mailboxACLCommand.getEntryKey(), 
mailboxACLCommand.getRights()));
+    private void assertUserHasAccessToShareeDomains(Username user, ACLCommand 
mailboxACLCommand) throws DifferentDomainException {
+        assertUserHasAccessToShareeDomains(user, 
ImmutableMap.of(mailboxACLCommand.getEntryKey(), 
mailboxACLCommand.getRights()));
     }
 
     public boolean isReadWrite(MailboxSession session, Mailbox mailbox, Flags 
sharedPermanentFlags) {
@@ -263,7 +266,7 @@ public class StoreRightManager implements RightManager {
 
     @Override
     public void setRights(MailboxPath mailboxPath, MailboxACL mailboxACL, 
MailboxSession session) throws MailboxException {
-        assertSharesBelongsToUserDomain(mailboxPath.getUser(), 
mailboxACL.getEntries());
+        assertUserHasAccessToShareeDomains(mailboxPath.getUser(), 
mailboxACL.getEntries());
 
         MailboxMapper mapper = 
mailboxSessionMapperFactory.getMailboxMapper(session);
         block(mapper.findMailboxByPath(mailboxPath)
@@ -285,7 +288,13 @@ public class StoreRightManager implements RightManager {
     }
 
     @VisibleForTesting
-    void assertSharesBelongsToUserDomain(Username user, Map<EntryKey, 
Rfc4314Rights> entries) throws DifferentDomainException {
+    void assertUserHasAccessToShareeDomains(Username user, Map<EntryKey, 
Rfc4314Rights> entries) throws DifferentDomainException {
+        if (IS_CROSS_DOMAIN_ACCESS_ALLOWED) {
+            // In this case, we impose no limitations.
+            return;
+        }
+
+        // If cross-domain sharing is disabled, a user may only access their 
own domain.
         if (entries.keySet().stream()
             .filter(entry -> !entry.getNameType().equals(NameType.special))
             .map(EntryKey::getName)
@@ -342,7 +351,7 @@ public class StoreRightManager implements RightManager {
      */
     public Mono<Void> setRightsReactiveWithoutAccessControl(MailboxPath 
mailboxPath, MailboxACL mailboxACL, MailboxSession session) {
         try {
-            assertSharesBelongsToUserDomain(mailboxPath.getUser(), 
mailboxACL.getEntries());
+            assertUserHasAccessToShareeDomains(mailboxPath.getUser(), 
mailboxACL.getEntries());
         } catch (DifferentDomainException e) {
             return Mono.error(e);
         }
diff --git 
a/mailbox/store/src/test/java/org/apache/james/mailbox/store/StoreRightManagerTest.java
 
b/mailbox/store/src/test/java/org/apache/james/mailbox/store/StoreRightManagerTest.java
index 6a8e189d1b..cb5b7f7b75 100644
--- 
a/mailbox/store/src/test/java/org/apache/james/mailbox/store/StoreRightManagerTest.java
+++ 
b/mailbox/store/src/test/java/org/apache/james/mailbox/store/StoreRightManagerTest.java
@@ -24,18 +24,23 @@ import static 
org.apache.james.mailbox.fixture.MailboxFixture.BOB;
 import static org.apache.james.mailbox.fixture.MailboxFixture.CEDRIC;
 import static org.apache.james.mailbox.fixture.MailboxFixture.INBOX_ALICE;
 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 static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 import jakarta.mail.Flags;
 
 import org.apache.james.core.Username;
+import org.apache.james.events.Event;
 import org.apache.james.events.EventBus;
 import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.MailboxSessionUtil;
+import org.apache.james.mailbox.acl.ACLDiff;
 import org.apache.james.mailbox.acl.MailboxACLResolver;
 import org.apache.james.mailbox.acl.UnionMailboxACLResolver;
+import org.apache.james.mailbox.events.MailboxIdRegistrationKey;
 import org.apache.james.mailbox.exception.DifferentDomainException;
 import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.exception.MailboxNotFoundException;
@@ -65,6 +70,7 @@ class StoreRightManagerTest {
     MailboxSession aliceSession;
     MailboxACLResolver mailboxAclResolver;
     MailboxMapper mockedMailboxMapper;
+    EventBus eventBus;
 
     @BeforeEach
     void setup() {
@@ -72,7 +78,7 @@ class StoreRightManagerTest {
         MailboxSessionMapperFactory mockedMapperFactory = 
mock(MailboxSessionMapperFactory.class);
         mockedMailboxMapper = mock(MailboxMapper.class);
         mailboxAclResolver = new UnionMailboxACLResolver();
-        EventBus eventBus = mock(EventBus.class);
+        eventBus = mock(EventBus.class);
         when(mockedMapperFactory.getMailboxMapper(aliceSession))
             .thenReturn(mockedMailboxMapper);
 
@@ -259,22 +265,37 @@ class StoreRightManagerTest {
     }
 
     @Test
-    void assertSharesBelongsToUserDomainShouldThrowWhenOneDomainIsDifferent() 
throws Exception  {
+    void 
assertUserHasAccessToShareeDomainsShouldThrowWhenOneDomainIsDifferent() throws 
Exception  {
         MailboxACL mailboxACL = new MailboxACL(new 
MailboxACL.Entry("a...@domain.org", Right.Write), 
                 new MailboxACL.Entry("b...@otherdomain.org", Right.Write), 
                 new MailboxACL.Entry("c...@domain.org", Right.Write));
         
-        assertThatThrownBy(() -> 
storeRightManager.assertSharesBelongsToUserDomain(Username.of("u...@domain.org"),
 mailboxACL.getEntries()))
+        assertThatThrownBy(() -> 
storeRightManager.assertUserHasAccessToShareeDomains(Username.of("u...@domain.org"),
 mailboxACL.getEntries()))
             .isInstanceOf(DifferentDomainException.class);
     }
 
     @Test
-    void 
assertSharesBelongsToUserDomainShouldNotThrowWhenDomainsAreIdentical() throws 
Exception  {
+    void 
assertUserHasAccessToShareeDomainsShouldNotThrowWhenDomainsAreIdentical() 
throws Exception  {
         MailboxACL mailboxACL = new MailboxACL(new 
MailboxACL.Entry("a...@domain.org", Right.Write), 
                 new MailboxACL.Entry("b...@domain.org", Right.Write), 
                 new MailboxACL.Entry("c...@domain.org", Right.Write));
         
-        
storeRightManager.assertSharesBelongsToUserDomain(Username.of("u...@domain.org"),
 mailboxACL.getEntries());
+        
storeRightManager.assertUserHasAccessToShareeDomains(Username.of("u...@domain.org"),
 mailboxACL.getEntries());
+    }
+
+    @Test
+    void 
assertUserHasAccessToShareDomainsShouldNotThrowOnDifferentDomainsWhenCrossDomainAccessEnabled()
 throws Exception  {
+        try {
+            StoreRightManager.IS_CROSS_DOMAIN_ACCESS_ALLOWED = true;
+
+            MailboxACL mailboxACL = new MailboxACL(new 
MailboxACL.Entry("a...@domain.org", Right.Write),
+                    new MailboxACL.Entry("b...@otherdomain.org", Right.Write),
+                    new MailboxACL.Entry("c...@domain.org", Right.Write));
+
+            
storeRightManager.assertUserHasAccessToShareeDomains(Username.of("u...@domain.org"),
 mailboxACL.getEntries());
+        } finally {
+            StoreRightManager.IS_CROSS_DOMAIN_ACCESS_ALLOWED = false;
+        }
     }
 
     @Test
@@ -288,4 +309,30 @@ class StoreRightManagerTest {
         assertThatThrownBy(() -> 
storeRightManager.applyRightsCommand(mailboxPath, aclCommand, aliceSession))
             .isInstanceOf(DifferentDomainException.class);
     }
+
+    @Test
+    void 
applyRightsCommandShouldNotThrowOnDifferentDomainsWhenCrossDomainEnabled() 
throws MailboxException {
+        try {
+            StoreRightManager.IS_CROSS_DOMAIN_ACCESS_ALLOWED = true;
+
+            MailboxPath mailboxPath = 
MailboxPath.forUser(Username.of("u...@domain.org"), "mailbox");
+            Mailbox mailbox = new Mailbox(mailboxPath, UID_VALIDITY, 
MAILBOX_ID);
+            mailbox.setACL(new MailboxACL(new 
MailboxACL.Entry(MailboxFixture.ALICE.asString(), Right.Administer)));
+            ACLCommand aclCommand = MailboxACL.command()
+                    .forUser(Username.of("otheru...@otherdomain.org"))
+                    .rights(Right.Read)
+                    .asAddition();
+
+            
when(mockedMailboxMapper.findMailboxByPath(mailboxPath)).thenReturn(Mono.just(mailbox));
+            when(mockedMailboxMapper.updateACL(mailbox, 
aclCommand)).thenReturn(Mono.just(ACLDiff.computeDiff(MailboxACL.EMPTY, new 
MailboxACL(
+                    new MailboxACL.Entry("u...@domain.org", Right.Read)
+            ))));
+            when(eventBus.dispatch(any(Event.class), 
any(MailboxIdRegistrationKey.class))).thenReturn(Mono.empty());
+
+            assertThatCode(() -> 
storeRightManager.applyRightsCommand(mailboxPath, aclCommand, aliceSession))
+                    .doesNotThrowAnyException();
+        } finally {
+            StoreRightManager.IS_CROSS_DOMAIN_ACCESS_ALLOWED = false;
+        }
+    }
 }
\ No newline at end of file
diff --git a/server/apps/cassandra-app/docker-configuration/jvm.properties 
b/server/apps/cassandra-app/docker-configuration/jvm.properties
index f638855ef4..85e932e7c9 100644
--- a/server/apps/cassandra-app/docker-configuration/jvm.properties
+++ b/server/apps/cassandra-app/docker-configuration/jvm.properties
@@ -49,4 +49,7 @@ sun.rmi.dgc.client.gcInterval=3600000000
 
 # Relax validating `*` and `%` characters in the mailbox name. Defaults to 
false.
 # Be careful turning on this as `%` and `*` are ambiguous for the LIST / LSUB 
commands that interpret those as wildcard thus returning all mailboxes matching 
the pattern.
-#james.relaxed.mailbox.name.validation=true
\ No newline at end of file
+#james.relaxed.mailbox.name.validation=true
+
+# Allow users to have rights for shares of different domain. Defaults to false.
+#james.rights.crossdomain.allow=false
\ No newline at end of file
diff --git a/server/apps/cassandra-app/sample-configuration/jvm.properties 
b/server/apps/cassandra-app/sample-configuration/jvm.properties
index 3cc3d17386..82ea594769 100644
--- a/server/apps/cassandra-app/sample-configuration/jvm.properties
+++ b/server/apps/cassandra-app/sample-configuration/jvm.properties
@@ -72,4 +72,7 @@ jmx.remote.x.mlet.allow.getMBeansFromURL=false
 
 # Relax validating `*` and `%` characters in the mailbox name. Defaults to 
false.
 # Be careful turning on this as `%` and `*` are ambiguous for the LIST / LSUB 
commands that interpret those as wildcard thus returning all mailboxes matching 
the pattern.
-#james.relaxed.mailbox.name.validation=true
\ No newline at end of file
+#james.relaxed.mailbox.name.validation=true
+
+# Allow users to have rights for shares of different domain. Defaults to false.
+#james.rights.crossdomain.allow=false
\ No newline at end of file
diff --git a/server/apps/distributed-app/docker-configuration/jvm.properties 
b/server/apps/distributed-app/docker-configuration/jvm.properties
index b7183468f0..573d064bcc 100644
--- a/server/apps/distributed-app/docker-configuration/jvm.properties
+++ b/server/apps/distributed-app/docker-configuration/jvm.properties
@@ -49,4 +49,7 @@ sun.rmi.dgc.client.gcInterval=3600000000
 
 # Relax validating `*` and `%` characters in the mailbox name. Defaults to 
false.
 # Be careful turning on this as `%` and `*` are ambiguous for the LIST / LSUB 
commands that interpret those as wildcard thus returning all mailboxes matching 
the pattern.
-#james.relaxed.mailbox.name.validation=true
\ No newline at end of file
+#james.relaxed.mailbox.name.validation=true
+
+# Allow users to have rights for shares of different domain. Defaults to false.
+#james.rights.crossdomain.allow=false
\ No newline at end of file
diff --git a/server/apps/distributed-app/sample-configuration/jvm.properties 
b/server/apps/distributed-app/sample-configuration/jvm.properties
index 3b20e8ddc7..a98626760d 100644
--- a/server/apps/distributed-app/sample-configuration/jvm.properties
+++ b/server/apps/distributed-app/sample-configuration/jvm.properties
@@ -96,4 +96,7 @@ jmx.remote.x.mlet.allow.getMBeansFromURL=false
 # james.deduplicating.blobstore.file.threshold=10240
 
 # From which point should range operation be used? Defaults to 3.
-# james.jmap.email.set.range.threshold=3
\ No newline at end of file
+# james.jmap.email.set.range.threshold=3
+
+# Allow users to have rights for shares of different domain. Defaults to false.
+#james.rights.crossdomain.allow=false
diff --git 
a/server/apps/distributed-pop3-app/docker-configuration/jvm.properties 
b/server/apps/distributed-pop3-app/docker-configuration/jvm.properties
index f638855ef4..85e932e7c9 100644
--- a/server/apps/distributed-pop3-app/docker-configuration/jvm.properties
+++ b/server/apps/distributed-pop3-app/docker-configuration/jvm.properties
@@ -49,4 +49,7 @@ sun.rmi.dgc.client.gcInterval=3600000000
 
 # Relax validating `*` and `%` characters in the mailbox name. Defaults to 
false.
 # Be careful turning on this as `%` and `*` are ambiguous for the LIST / LSUB 
commands that interpret those as wildcard thus returning all mailboxes matching 
the pattern.
-#james.relaxed.mailbox.name.validation=true
\ No newline at end of file
+#james.relaxed.mailbox.name.validation=true
+
+# Allow users to have rights for shares of different domain. Defaults to false.
+#james.rights.crossdomain.allow=false
\ No newline at end of file
diff --git 
a/server/apps/distributed-pop3-app/sample-configuration/jvm.properties 
b/server/apps/distributed-pop3-app/sample-configuration/jvm.properties
index 2612e150ff..3901fdf50c 100644
--- a/server/apps/distributed-pop3-app/sample-configuration/jvm.properties
+++ b/server/apps/distributed-pop3-app/sample-configuration/jvm.properties
@@ -62,4 +62,7 @@ jmx.remote.x.mlet.allow.getMBeansFromURL=false
 
 # Relax validating `*` and `%` characters in the mailbox name. Defaults to 
false.
 # Be careful turning on this as `%` and `*` are ambiguous for the LIST / LSUB 
commands that interpret those as wildcard thus returning all mailboxes matching 
the pattern.
-#james.relaxed.mailbox.name.validation=true
\ No newline at end of file
+#james.relaxed.mailbox.name.validation=true
+
+# Allow users to have rights for shares of different domain. Defaults to false.
+#james.rights.crossdomain.allow=false
\ No newline at end of file
diff --git a/server/apps/jpa-app/sample-configuration/jvm.properties 
b/server/apps/jpa-app/sample-configuration/jvm.properties
index 68859135ac..d6d2e0ae4b 100644
--- a/server/apps/jpa-app/sample-configuration/jvm.properties
+++ b/server/apps/jpa-app/sample-configuration/jvm.properties
@@ -60,4 +60,7 @@ openjpa.Multithreaded=true
 
 # Relax validating `*` and `%` characters in the mailbox name. Defaults to 
false.
 # Be careful turning on this as `%` and `*` are ambiguous for the LIST / LSUB 
commands that interpret those as wildcard thus returning all mailboxes matching 
the pattern.
-#james.relaxed.mailbox.name.validation=true
\ No newline at end of file
+#james.relaxed.mailbox.name.validation=true
+
+# Allow users to have rights for shares of different domain. Defaults to false.
+#james.rights.crossdomain.allow=false
\ No newline at end of file
diff --git a/server/apps/memory-app/sample-configuration/jvm.properties 
b/server/apps/memory-app/sample-configuration/jvm.properties
index 692bc6bf46..317261dd03 100644
--- a/server/apps/memory-app/sample-configuration/jvm.properties
+++ b/server/apps/memory-app/sample-configuration/jvm.properties
@@ -62,4 +62,7 @@ jmx.remote.x.mlet.allow.getMBeansFromURL=false
 
 # Relax validating `*` and `%` characters in the mailbox name. Defaults to 
false.
 # Be careful turning on this as `%` and `*` are ambiguous for the LIST / LSUB 
commands that interpret those as wildcard thus returning all mailboxes matching 
the pattern.
-#james.relaxed.mailbox.name.validation=true
\ No newline at end of file
+#james.relaxed.mailbox.name.validation=true
+
+# Allow users to have rights for shares of different domain. Defaults to false.
+#james.rights.crossdomain.allow=false
\ 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