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 <[email protected]>
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("[email protected]", Right.Write),
new MailboxACL.Entry("[email protected]", Right.Write),
new MailboxACL.Entry("[email protected]", Right.Write));
- assertThatThrownBy(() ->
storeRightManager.assertSharesBelongsToUserDomain(Username.of("[email protected]"),
mailboxACL.getEntries()))
+ assertThatThrownBy(() ->
storeRightManager.assertUserHasAccessToShareeDomains(Username.of("[email protected]"),
mailboxACL.getEntries()))
.isInstanceOf(DifferentDomainException.class);
}
@Test
- void
assertSharesBelongsToUserDomainShouldNotThrowWhenDomainsAreIdentical() throws
Exception {
+ void
assertUserHasAccessToShareeDomainsShouldNotThrowWhenDomainsAreIdentical()
throws Exception {
MailboxACL mailboxACL = new MailboxACL(new
MailboxACL.Entry("[email protected]", Right.Write),
new MailboxACL.Entry("[email protected]", Right.Write),
new MailboxACL.Entry("[email protected]", Right.Write));
-
storeRightManager.assertSharesBelongsToUserDomain(Username.of("[email protected]"),
mailboxACL.getEntries());
+
storeRightManager.assertUserHasAccessToShareeDomains(Username.of("[email protected]"),
mailboxACL.getEntries());
+ }
+
+ @Test
+ void
assertUserHasAccessToShareDomainsShouldNotThrowOnDifferentDomainsWhenCrossDomainAccessEnabled()
throws Exception {
+ try {
+ StoreRightManager.IS_CROSS_DOMAIN_ACCESS_ALLOWED = true;
+
+ MailboxACL mailboxACL = new MailboxACL(new
MailboxACL.Entry("[email protected]", Right.Write),
+ new MailboxACL.Entry("[email protected]", Right.Write),
+ new MailboxACL.Entry("[email protected]", Right.Write));
+
+
storeRightManager.assertUserHasAccessToShareeDomains(Username.of("[email protected]"),
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("[email protected]"), "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("[email protected]"))
+ .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("[email protected]", 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: [email protected]
For additional commands, e-mail: [email protected]