Don't let a group's members share its mailboxes on #146
No files matched your search
@@ -553,6 +553,16 @@ impl AccessToken {
|
||||
|| self.inner.access_to.iter().any(|a| a.account_id == account_id)
|
||||
}
|
||||
|
||||
/// inbuxa: MA-D0: in the account only because it is a group this token
|
||||
/// belongs to. Such a member has the group's mailbox but may not share it
|
||||
/// on: who is in a group is an administrator's decision, and a share
|
||||
/// would let anyone in.
|
||||
pub fn is_group_member_only(&self, account_id: u32) -> bool {
|
||||
self.inner.account_id != account_id
|
||||
&& self.inner.member_of.contains(&account_id)
|
||||
&& !self.has_permission(Permission::Impersonate)
|
||||
}
|
||||
|
||||
pub fn is_account_id(&self, account_id: u32) -> bool {
|
||||
self.inner.account_id == account_id
|
||||
}
|
||||
|
||||
@@ -212,7 +212,7 @@ impl<T: SessionStream> Session<T> {
|
||||
}
|
||||
rights
|
||||
} else {
|
||||
vec![
|
||||
let mut rights = vec![
|
||||
Rights::Read,
|
||||
Rights::Lookup,
|
||||
Rights::Insert,
|
||||
@@ -223,8 +223,12 @@ impl<T: SessionStream> Session<T> {
|
||||
Rights::CreateMailbox,
|
||||
Rights::DeleteMailbox,
|
||||
Rights::Post,
|
||||
Rights::Administer,
|
||||
]
|
||||
];
|
||||
// inbuxa: MA-D0: a group's members don't share its mailboxes on.
|
||||
if !access_token.is_group_member_only(mailbox_id.account_id) {
|
||||
rights.push(Rights::Administer);
|
||||
}
|
||||
rights
|
||||
};
|
||||
|
||||
trc::event!(
|
||||
@@ -266,10 +270,20 @@ impl<T: SessionStream> Session<T> {
|
||||
|
||||
spawn_op!(data, {
|
||||
// Validate mailbox
|
||||
let (mailbox_id, current_mailbox, _) = data
|
||||
let (mailbox_id, current_mailbox, access_token) = data
|
||||
.get_acl_mailbox(&arguments, true)
|
||||
.await
|
||||
.imap_ctx(&arguments.tag, trc::location!())?;
|
||||
|
||||
// inbuxa: MA-D0: a group's members don't share its mailboxes on.
|
||||
if access_token.is_group_member_only(mailbox_id.account_id) {
|
||||
return Err(trc::ImapEvent::Error
|
||||
.into_err()
|
||||
.details("This mailbox belongs to a group. Only an administrator can change who has it.")
|
||||
.code(ResponseCode::NoPerm)
|
||||
.id(arguments.tag.to_string()));
|
||||
}
|
||||
|
||||
let current_mailbox = current_mailbox
|
||||
.into_deserialized::<email::mailbox::Mailbox>()
|
||||
.imap_ctx(&arguments.tag, trc::location!())?;
|
||||
|
||||
@@ -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>()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -713,6 +713,35 @@ pub async fn test(test: &TestServer) {
|
||||
.await,
|
||||
);
|
||||
|
||||
// inbuxa: MA-D0: a member can't share the group's mailbox on, and isn't
|
||||
// told it may. Who is in a group is an administrator's decision.
|
||||
assert_forbidden(
|
||||
john_client
|
||||
.set_default_account_id(sales.id_string())
|
||||
.mailbox_update_acl(&inbox_id, bill.id_string(), [ACL::ReadItems])
|
||||
.await,
|
||||
);
|
||||
assert!(
|
||||
!john_client
|
||||
.set_default_account_id(sales.id_string())
|
||||
.mailbox_get(&inbox_id, [mailbox::Property::MyRights].into())
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.my_rights()
|
||||
.unwrap()
|
||||
.acl_list()
|
||||
.contains(&ACL::Administer)
|
||||
);
|
||||
bill_client.refresh_session().await.unwrap();
|
||||
assert!(bill_client.session().account(sales.id_string()).is_none());
|
||||
assert_forbidden(
|
||||
bill_client
|
||||
.set_default_account_id(sales.id_string())
|
||||
.email_get(&email_id, [Property::Subject].into())
|
||||
.await,
|
||||
);
|
||||
|
||||
// Remove John from the sales group
|
||||
admin
|
||||
.registry_update_object(
|
||||
|
||||
Reference in new issue
Block a user