Fix JMAP: Thread/get did not filter by per-mailbox ACLs on shared accounts

This commit is contained in:
Maurus Decimus
2026-05-12 16:05:45 +02:00
parent 415036dd64
commit bf4d199606
4 changed files with 25 additions and 13 deletions

View File

@@ -12,6 +12,7 @@ If you are upgrading from v0.16.x, replace the binary (or run `docker pull`). If
## Fixed
- DAV: `acl-principal-prop-set` REPORT enforced the wrong privilege.
- JMAP: `Thread/get` did not filter by per-mailbox ACLs on shared accounts.
## [0.16.5] - 2026-05-11

View File

@@ -47,11 +47,11 @@ impl DnsUpdater {
core,
updater: dns_update::DnsUpdater::new_rfc2136_tsig(
match server.protocol {
enums::IpProtocol::Udp => DnsAddress::Tcp(SocketAddr::new(
enums::IpProtocol::Tcp => DnsAddress::Tcp(SocketAddr::new(
server.host.into_inner(),
server.port as u16,
)),
enums::IpProtocol::Tcp => DnsAddress::Udp(SocketAddr::new(
enums::IpProtocol::Udp => DnsAddress::Udp(SocketAddr::new(
server.host.into_inner(),
server.port as u16,
)),

View File

@@ -247,7 +247,7 @@ impl RequestHandler for Server {
set_account_id_if_missing(&mut req.account_id, access_token);
access_token.assert_has_access(req.account_id, Collection::Email)?;
self.thread_get(*req).await?.into()
self.thread_get(*req, access_token).await?.into()
}
GetRequestMethod::Identity(mut req) => {
set_account_id_if_missing(&mut req.account_id, access_token);

View File

@@ -5,8 +5,8 @@
*/
use crate::changes::state::StateManager;
use common::Server;
use email::cache::MessageCacheFetch;
use common::{Server, auth::AccessToken};
use email::cache::{MessageCacheFetch, email::MessageCacheAccess};
use jmap_proto::{
method::get::{GetRequest, GetResponse},
object::thread::{Thread, ThreadProperty, ThreadValue},
@@ -21,12 +21,13 @@ use store::{
write::SearchIndex,
};
use trc::AddContext;
use types::{collection::SyncCollection, id::Id};
use types::{acl::Acl, collection::SyncCollection, id::Id};
pub trait ThreadGet: Sync + Send {
fn thread_get(
&self,
request: GetRequest<Thread>,
access_token: &AccessToken,
) -> impl Future<Output = trc::Result<GetResponse<Thread>>> + Send;
}
@@ -34,17 +35,27 @@ impl ThreadGet for Server {
async fn thread_get(
&self,
mut request: GetRequest<Thread>,
access_token: &AccessToken,
) -> trc::Result<GetResponse<Thread>> {
let account_id = request.account_id.document_id();
let mut thread_map: AHashMap<u32, RoaringBitmap> = AHashMap::with_capacity(32);
let mut all_ids = RoaringBitmap::new();
for item in &self
let cache = self
.get_cached_messages(account_id)
.await
.caused_by(trc::location!())?
.emails
.items
{
.caused_by(trc::location!())?;
let shared_ids = if access_token.is_shared(account_id) {
Some(cache.shared_messages(access_token, Acl::ReadItems))
} else {
None
};
let mut thread_map: AHashMap<u32, RoaringBitmap> = AHashMap::with_capacity(32);
let mut all_ids = RoaringBitmap::new();
for item in &cache.emails.items {
if shared_ids
.as_ref()
.is_some_and(|ids| !ids.contains(item.document_id))
{
continue;
}
thread_map
.entry(item.thread_id)
.or_default()