Don't let a group's members share its mailboxes on
ci / github (pull_request) Skipped
ci / fork-checks (pull_request) Failing after 1m16s
ci / build (pull_request) Canceled after 5m6s

A group's members reach its mailbox through membership, which counts
as owning the account, so every ACL check was skipped: on a scratch
server a member gave an outsider read access to the group's Inbox with
one Mailbox/set shareWith, with no administrator involved and nothing
audited. Who is in a group is an administrator's decision.

AccessToken::is_group_member_only names that case (in the account
only through a group, without Impersonate). For such a member:

- Mailbox/set with a shareWith change, on create or update, is
  refused as forbidden;
- IMAP SETACL and DELETEACL answer NO [NOPERM];
- myRights reports mayShare false, and MYRIGHTS leaves out "a";
  every other right stays.

Administrators and the account itself are unchanged. The JMAP ACL
test's group section now checks all three for a member and that the
outsider still has nothing (specs/multi-account.md, MA-D0, G1).

jmap_tests and imap_tests pass (RocksDB). The IMAP refusal has no test
of its own yet; imap_tests passing shows the rest is unchanged.
This commit is contained in:
jcoffey-dev committed 2026-10-05 14:15:58 -07:00
1 parent d7bebd454d
commit 2d8728793c
5 files changed
+72 -4

No files matched your search

+6
View File
@@ -14,6 +14,7 @@ use jmap_tools::{Map, Value};
use std::future::Future;
use store::ahash::AHashSet;
use types::{acl::Acl, collection::Collection, keyword::Keyword, special_use::SpecialUse};
use utils::map::bitmap::Bitmap;
use crate::{api::acl::JmapRights, changes::state::JmapCacheState};
@@ -138,6 +139,11 @@ impl MailboxGet for Server {
JmapRights::rights::<Mailbox>(
cached_mailbox.acls.as_slice().effective_acl(access_token),
)
} else if access_token.is_group_member_only(account_id) {
// inbuxa: MA-D0: everything but sharing it on.
let mut acl = Bitmap::<Acl>::all();
acl.remove(Acl::Share);
JmapRights::rights::<Mailbox>(acl)
} else {
JmapRights::all_rights::<Mailbox>()
}
+9
View File
@@ -613,6 +613,15 @@ impl MailboxSet for Server {
// Refresh ACLs
let current = update.map(|(_, current)| current);
if has_acl_changes {
// inbuxa: MA-D0: a group's members don't share its mailboxes on.
if ctx.access_token.is_group_member_only(ctx.account_id) {
return Ok(Err(SetError::forbidden()
.with_property(MailboxProperty::ShareWith)
.with_description(
"This mailbox belongs to a group. Only an administrator can change who has it.",
)));
}
if !changes.acls.is_empty()
&& let Err(err) = self.acl_validate(ctx.account_id, &changes.acls).await
{