mirror of
https://github.com/stalwartlabs/mail-server.git
synced 2025-12-11 13:56:27 +08:00
JMAP protocol layer refactoring (passing tests)
This commit is contained in:
parent
c7c996d738
commit
c8e0a1b2cb
24 changed files with 528 additions and 553 deletions
778
Cargo.lock
generated
778
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -147,7 +147,7 @@ pub async fn fetch_mailboxes(
|
|||
mailbox::Property::ParentId,
|
||||
mailbox::Property::Role,
|
||||
mailbox::Property::SortOrder,
|
||||
mailbox::Property::ACL,
|
||||
mailbox::Property::ShareWith,
|
||||
]);
|
||||
|
||||
let mut response = request
|
||||
|
|
|
|||
|
|
@ -527,9 +527,9 @@ async fn import_mailboxes(
|
|||
if mailbox.sort_order() > 0 {
|
||||
create_request.sort_order(mailbox.sort_order());
|
||||
}
|
||||
if let Some(acls) = mailbox.acl() {
|
||||
/*if let Some(acls) = mailbox.acl() {
|
||||
create_request.acls(acls.clone().into_iter());
|
||||
}
|
||||
}*/
|
||||
if mailbox.is_subscribed() {
|
||||
create_request.is_subscribed(true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ store = { path = "../store" }
|
|||
utils = { path = "../utils" }
|
||||
types = { path = "../types" }
|
||||
trc = { path = "../trc" }
|
||||
jmap-tools = { path = "/Users/me/code/jmap-tool" }
|
||||
jmap-tools = { version = "0.1" }
|
||||
mail-parser = { version = "0.11", features = ["full_encoding", "rkyv"] }
|
||||
serde = { version = "1.0", features = ["derive"]}
|
||||
ahash = { version = "0.8.2", features = ["serde"] }
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ impl<'de> DeserializeArguments<'de> for ChangesRequest {
|
|||
b"accountId" => {
|
||||
self.account_id = map.next_value()?;
|
||||
},
|
||||
b"sinceQueryState" => {
|
||||
b"sinceState" => {
|
||||
self.since_state = map.next_value()?;
|
||||
},
|
||||
b"maxChanges" => {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
use crate::{
|
||||
error::set::SetError,
|
||||
method::JmapDict,
|
||||
object::{
|
||||
AnyId,
|
||||
email::{EmailProperty, EmailValue},
|
||||
|
|
@ -93,13 +94,13 @@ impl<'de> DeserializeArguments<'de> for ImportEmail {
|
|||
self.blob_id = map.next_value()?;
|
||||
},
|
||||
b"keywords" => {
|
||||
self.keywords = map.next_value()?;
|
||||
self.keywords = map.next_value::<JmapDict<Keyword>>()?.0;
|
||||
},
|
||||
b"receivedAt" => {
|
||||
self.received_at = map.next_value()?;
|
||||
},
|
||||
b"mailboxIds" => {
|
||||
self.mailbox_ids = MaybeResultReference::Value(map.next_value::<Vec<MaybeIdReference<Id>>>()?);
|
||||
self.mailbox_ids = MaybeResultReference::Value(map.next_value::<JmapDict<MaybeIdReference<Id>>>()?.0);
|
||||
},
|
||||
b"#mailboxIds" => {
|
||||
self.mailbox_ids = MaybeResultReference::Reference(map.next_value::<ResultReference>()?);
|
||||
|
|
|
|||
|
|
@ -6,6 +6,11 @@
|
|||
|
||||
use ahash::AHashMap;
|
||||
use jmap_tools::Property;
|
||||
use serde::{
|
||||
Deserialize, Deserializer,
|
||||
de::{self, MapAccess, Visitor},
|
||||
};
|
||||
use std::{borrow::Cow, fmt, str::FromStr};
|
||||
|
||||
pub mod changes;
|
||||
pub mod copy;
|
||||
|
|
@ -37,3 +42,44 @@ impl<T: Property + serde::Serialize> From<T> for PropertyWrapper<T> {
|
|||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct JmapDict<T: FromStr>(pub Vec<T>);
|
||||
|
||||
struct JmapDictVisitor<'de, T: FromStr> {
|
||||
marker: std::marker::PhantomData<&'de T>,
|
||||
}
|
||||
|
||||
impl<'de, T: FromStr> Visitor<'de> for JmapDictVisitor<'de, T> {
|
||||
type Value = JmapDict<T>;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("a map")
|
||||
}
|
||||
|
||||
fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
|
||||
where
|
||||
M: MapAccess<'de>,
|
||||
{
|
||||
let mut vec = Vec::with_capacity(3);
|
||||
|
||||
while let Some(key) = access.next_key::<Cow<'de, str>>()? {
|
||||
let key = T::from_str(&key).map_err(|_| de::Error::custom("invalid dictionary key"))?;
|
||||
if access.next_value::<Option<bool>>()?.unwrap_or(false) {
|
||||
vec.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(JmapDict(vec))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, T: FromStr + 'static> Deserialize<'de> for JmapDict<T> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_map(JmapDictVisitor {
|
||||
marker: std::marker::PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,6 +112,7 @@ impl Property for EmailProperty {
|
|||
if let Some(Key::Property(key)) = key {
|
||||
match key.patch_or_prop() {
|
||||
EmailProperty::Keywords => EmailProperty::Keyword(Keyword::parse(value)).into(),
|
||||
EmailProperty::MailboxIds => Id::from_str(value).ok().map(EmailProperty::IdValue),
|
||||
_ => EmailProperty::parse(value, allow_patch),
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -114,13 +114,14 @@ impl Element for EmailSubmissionValue {
|
|||
fn try_parse<P>(key: &Key<'_, Self::Property>, value: &str) -> Option<Self> {
|
||||
if let Key::Property(prop) = key {
|
||||
match prop.patch_or_prop() {
|
||||
EmailSubmissionProperty::Id | EmailSubmissionProperty::ThreadId => {
|
||||
match parse_ref(value) {
|
||||
MaybeReference::Value(v) => Some(EmailSubmissionValue::Id(v)),
|
||||
MaybeReference::Reference(v) => Some(EmailSubmissionValue::IdReference(v)),
|
||||
MaybeReference::ParseError => None,
|
||||
}
|
||||
}
|
||||
EmailSubmissionProperty::Id
|
||||
| EmailSubmissionProperty::ThreadId
|
||||
| EmailSubmissionProperty::IdentityId
|
||||
| EmailSubmissionProperty::EmailId => match parse_ref(value) {
|
||||
MaybeReference::Value(v) => Some(EmailSubmissionValue::Id(v)),
|
||||
MaybeReference::Reference(v) => Some(EmailSubmissionValue::IdReference(v)),
|
||||
MaybeReference::ParseError => None,
|
||||
},
|
||||
EmailSubmissionProperty::MdnBlobIds | EmailSubmissionProperty::DsnBlobIds => {
|
||||
match parse_ref(value) {
|
||||
MaybeReference::Value(v) => Some(EmailSubmissionValue::BlobId(v)),
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ pub enum MailboxRight {
|
|||
MayRename,
|
||||
MaySubmit,
|
||||
MayDelete,
|
||||
MayShare,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
|
|
@ -62,10 +63,16 @@ pub enum MailboxValue {
|
|||
|
||||
impl Property for MailboxProperty {
|
||||
fn try_parse(key: Option<&Key<'_, Self>>, value: &str) -> Option<Self> {
|
||||
if let Some(Key::Property(MailboxProperty::ShareWith)) = key {
|
||||
Id::from_str(value).ok().map(MailboxProperty::IdValue)
|
||||
let allow_patch = key.is_none();
|
||||
if let Some(Key::Property(key)) = key {
|
||||
match key.patch_or_prop() {
|
||||
MailboxProperty::ShareWith => {
|
||||
Id::from_str(value).ok().map(MailboxProperty::IdValue)
|
||||
}
|
||||
_ => MailboxProperty::parse(value, allow_patch),
|
||||
}
|
||||
} else {
|
||||
MailboxProperty::parse(value, key.is_none())
|
||||
MailboxProperty::parse(value, allow_patch)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -103,6 +110,7 @@ impl MailboxRight {
|
|||
MailboxRight::MayRename => "mayRename",
|
||||
MailboxRight::MaySubmit => "maySubmit",
|
||||
MailboxRight::MayDelete => "mayDelete",
|
||||
MailboxRight::MayShare => "mayShare",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -158,6 +166,7 @@ impl MailboxProperty {
|
|||
b"mayRename" => MailboxProperty::Rights(MailboxRight::MayRename),
|
||||
b"maySubmit" => MailboxProperty::Rights(MailboxRight::MaySubmit),
|
||||
b"mayDelete" => MailboxProperty::Rights(MailboxRight::MayDelete),
|
||||
b"mayShare" => MailboxProperty::Rights(MailboxRight::MayShare),
|
||||
b"isSubscribed" => MailboxProperty::IsSubscribed,
|
||||
)
|
||||
.or_else(|| {
|
||||
|
|
@ -460,21 +469,23 @@ impl JmapRight for MailboxRight {
|
|||
Acl::Modify => &[MailboxRight::MayRename],
|
||||
Acl::Submit => &[MailboxRight::MaySubmit],
|
||||
Acl::Delete => &[MailboxRight::MayDelete],
|
||||
Acl::Administer => &[MailboxRight::MayShare],
|
||||
_ => &[],
|
||||
}
|
||||
}
|
||||
|
||||
fn to_acl(&self) -> Acl {
|
||||
fn to_acl(&self) -> &'static [Acl] {
|
||||
match self {
|
||||
MailboxRight::MayReadItems => Acl::ReadItems,
|
||||
MailboxRight::MayAddItems => Acl::AddItems,
|
||||
MailboxRight::MayRemoveItems => Acl::RemoveItems,
|
||||
MailboxRight::MaySetSeen => Acl::ModifyItems,
|
||||
MailboxRight::MaySetKeywords => Acl::ModifyItems,
|
||||
MailboxRight::MayCreateChild => Acl::CreateChild,
|
||||
MailboxRight::MayRename => Acl::Modify,
|
||||
MailboxRight::MaySubmit => Acl::Submit,
|
||||
MailboxRight::MayDelete => Acl::Delete,
|
||||
MailboxRight::MayReadItems => &[Acl::Read, Acl::ReadItems],
|
||||
MailboxRight::MayAddItems => &[Acl::AddItems],
|
||||
MailboxRight::MayRemoveItems => &[Acl::RemoveItems],
|
||||
MailboxRight::MaySetSeen => &[Acl::ModifyItems],
|
||||
MailboxRight::MaySetKeywords => &[Acl::ModifyItems],
|
||||
MailboxRight::MayCreateChild => &[Acl::CreateChild],
|
||||
MailboxRight::MayRename => &[Acl::Modify],
|
||||
MailboxRight::MaySubmit => &[Acl::Submit],
|
||||
MailboxRight::MayDelete => &[Acl::Delete],
|
||||
MailboxRight::MayShare => &[Acl::Administer],
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -489,6 +500,7 @@ impl JmapRight for MailboxRight {
|
|||
MailboxRight::MayRename,
|
||||
MailboxRight::MaySubmit,
|
||||
MailboxRight::MayDelete,
|
||||
MailboxRight::MayShare,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ pub trait JmapObject: std::fmt::Debug {
|
|||
}
|
||||
|
||||
pub trait JmapSharedObject: JmapObject {
|
||||
type Right: JmapRight + Into<Self::Property> + Debug + Sync + Send;
|
||||
type Right: JmapRight + Into<Self::Property> + Debug + Clone + Copy + Sync + Send;
|
||||
|
||||
const SHARE_WITH_PROPERTY: Self::Property;
|
||||
}
|
||||
|
|
@ -53,7 +53,7 @@ pub trait JmapSharedObject: JmapObject {
|
|||
pub trait JmapRight: Clone + Copy + Sized + 'static {
|
||||
fn from_acl(acl: Acl) -> &'static [Self];
|
||||
fn all_rights() -> &'static [Self];
|
||||
fn to_acl(&self) -> Acl;
|
||||
fn to_acl(&self) -> &'static [Acl];
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
|
||||
|
|
@ -169,7 +169,7 @@ impl JmapRight for Null {
|
|||
unreachable!()
|
||||
}
|
||||
|
||||
fn to_acl(&self) -> Acl {
|
||||
fn to_acl(&self) -> &'static [Acl] {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ use crate::{
|
|||
response::Response,
|
||||
};
|
||||
use compact_str::format_compact;
|
||||
use jmap_tools::{Element, Property, Value};
|
||||
use jmap_tools::{Element, Key, Property, Value};
|
||||
use std::collections::HashMap;
|
||||
use types::id::Id;
|
||||
|
||||
|
|
@ -310,6 +310,8 @@ where
|
|||
fn get_created_id(&self, id_ref: &str) -> Option<AnyId> {
|
||||
self.created
|
||||
.get(id_ref)
|
||||
.and_then(|v| v.as_object())
|
||||
.and_then(|v| v.get(&Key::Property(T::ID_PROPERTY)))
|
||||
.and_then(|v| v.as_element())
|
||||
.and_then(|v| v.as_any_id())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -175,7 +175,7 @@ impl<'de> Visitor<'de> for CallVisitor {
|
|||
}
|
||||
},
|
||||
(MethodFunction::Get, MethodObject::Blob) => match seq.next_element() {
|
||||
Ok(Some(value)) => RequestMethod::Get(GetRequestMethod::Email(value)),
|
||||
Ok(Some(value)) => RequestMethod::Get(GetRequestMethod::Blob(value)),
|
||||
Err(err) => RequestMethod::invalid(err),
|
||||
Ok(None) => {
|
||||
return Err(de::Error::invalid_length(1, &self));
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
use super::method::MethodName;
|
||||
use jmap_tools::{JsonPointer, Null};
|
||||
use std::{fmt::Display, str::FromStr};
|
||||
use std::{borrow::Cow, fmt::Display, str::FromStr};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct ResultReference {
|
||||
|
|
@ -54,17 +54,34 @@ impl<'de, V: FromStr> serde::Deserialize<'de> for MaybeIdReference<V> {
|
|||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let value = <&str>::deserialize(deserializer)?;
|
||||
let value = <Cow<'de, str>>::deserialize(deserializer)?;
|
||||
|
||||
if let Some(reference) = value.strip_prefix('#') {
|
||||
if reference.is_empty() {
|
||||
return Ok(MaybeIdReference::Invalid(value.to_string()));
|
||||
return Ok(MaybeIdReference::Invalid(value.into_owned()));
|
||||
}
|
||||
Ok(MaybeIdReference::Reference(reference.to_string()))
|
||||
} else if let Ok(id) = V::from_str(value) {
|
||||
} else if let Ok(id) = V::from_str(value.as_ref()) {
|
||||
Ok(MaybeIdReference::Id(id))
|
||||
} else {
|
||||
Ok(MaybeIdReference::Invalid(value.to_string()))
|
||||
Ok(MaybeIdReference::Invalid(value.into_owned()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: FromStr> FromStr for MaybeIdReference<V> {
|
||||
type Err = V::Err;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
if let Some(reference) = s.strip_prefix('#') {
|
||||
if reference.is_empty() {
|
||||
return Ok(MaybeIdReference::Invalid(s.to_string()));
|
||||
}
|
||||
Ok(MaybeIdReference::Reference(reference.to_string()))
|
||||
} else if let Ok(id) = V::from_str(s) {
|
||||
Ok(MaybeIdReference::Id(id))
|
||||
} else {
|
||||
Ok(MaybeIdReference::Invalid(s.to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -168,7 +168,7 @@ impl<'de> Visitor<'de> for WebSocketMessageVisitor {
|
|||
message_type = MessageType::parse(map.next_value()?);
|
||||
},
|
||||
b"dataTypes" => {
|
||||
push_enable.data_types = map.next_value()?;
|
||||
push_enable.data_types = map.next_value::<Option<Vec<DataType>>>()?.unwrap_or_default();
|
||||
found_push_keys = true;
|
||||
},
|
||||
b"pushState" => {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ mail-builder = { version = "0.4" }
|
|||
mail-send = { version = "0.5", default-features = false, features = ["cram-md5", "ring", "tls12"] }
|
||||
mail-auth = { version = "0.7.1", features = ["generate"] }
|
||||
sieve-rs = { version = "0.7", features = ["rkyv"] }
|
||||
jmap-tools = { path = "/Users/me/code/jmap-tool", features = ["rkyv"] }
|
||||
jmap-tools = { version = "0.1", features = ["rkyv"] }
|
||||
serde = { version = "1.0", features = ["derive"]}
|
||||
serde_json = "1.0"
|
||||
hyper = { version = "1.0.1", features = ["server", "http1", "http2"] }
|
||||
|
|
|
|||
|
|
@ -96,15 +96,20 @@ impl JmapRights {
|
|||
.ok_or_else(|| {
|
||||
SetError::invalid_properties()
|
||||
.with_property(T::SHARE_WITH_PROPERTY)
|
||||
.with_description("Invalid permission.")
|
||||
.with_description(format!(
|
||||
"Invalid permission {:?}.",
|
||||
right.to_cow().unwrap_or_default()
|
||||
))
|
||||
})?
|
||||
.to_acl();
|
||||
.to_acl()
|
||||
.iter()
|
||||
.copied();
|
||||
|
||||
if let Some(acl_item) = grants.iter_mut().find(|item| item.account_id == account_id) {
|
||||
if is_set {
|
||||
acl_item.grants.insert(acl);
|
||||
acl_item.grants.insert_many(acl);
|
||||
} else {
|
||||
acl_item.grants.remove(acl);
|
||||
acl_item.grants.insert_many(acl);
|
||||
if acl_item.grants.is_empty() {
|
||||
grants.retain(|item| item.account_id != account_id);
|
||||
}
|
||||
|
|
@ -112,7 +117,7 @@ impl JmapRights {
|
|||
} else if is_set {
|
||||
grants.push(AclGrant {
|
||||
account_id,
|
||||
grants: Bitmap::from_iter([acl]),
|
||||
grants: Bitmap::from_iter(acl),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
|
|
@ -145,15 +150,17 @@ impl JmapRights {
|
|||
let mut acls = Bitmap::new();
|
||||
|
||||
for key in value.into_expanded_boolean_set() {
|
||||
acls.insert(
|
||||
key.try_into_property()
|
||||
.and_then(|p| T::Right::try_from(p).ok())
|
||||
acls.insert_many(
|
||||
key.as_property()
|
||||
.and_then(|p| T::Right::try_from(p.clone()).ok())
|
||||
.ok_or_else(|| {
|
||||
SetError::invalid_properties()
|
||||
.with_property(T::SHARE_WITH_PROPERTY)
|
||||
.with_description("Invalid permission.")
|
||||
.with_description(format!("Invalid permission {:?}.", key.to_string()))
|
||||
})?
|
||||
.to_acl(),
|
||||
.to_acl()
|
||||
.iter()
|
||||
.copied(),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -177,7 +177,12 @@ impl IntoForm for HeaderValue<'_> {
|
|||
(
|
||||
HeaderValue::Address(mail_parser::Address::Group(grouplist)),
|
||||
HeaderForm::Addresses,
|
||||
) => from_mail_grouplist(grouplist),
|
||||
) => Value::Array(
|
||||
grouplist
|
||||
.into_iter()
|
||||
.flat_map(|group| group.addresses.into_iter().map(from_mail_addr))
|
||||
.collect(),
|
||||
),
|
||||
(
|
||||
HeaderValue::Address(mail_parser::Address::List(addrlist)),
|
||||
HeaderForm::GroupedAddresses,
|
||||
|
|
@ -190,7 +195,12 @@ impl IntoForm for HeaderValue<'_> {
|
|||
(
|
||||
HeaderValue::Address(mail_parser::Address::Group(grouplist)),
|
||||
HeaderForm::GroupedAddresses,
|
||||
) => from_mail_grouplist(grouplist),
|
||||
) => Value::Array(
|
||||
grouplist
|
||||
.into_iter()
|
||||
.map(from_mail_group)
|
||||
.collect::<Vec<Value<'static, EmailProperty, EmailValue>>>(),
|
||||
),
|
||||
|
||||
_ => Value::Null,
|
||||
}
|
||||
|
|
@ -513,12 +523,3 @@ fn from_mail_addrlist(addrlist: Vec<Addr<'_>>) -> Value<'static, EmailProperty,
|
|||
.collect::<Vec<Value<'static, EmailProperty, EmailValue>>>(),
|
||||
)
|
||||
}
|
||||
|
||||
fn from_mail_grouplist(grouplist: Vec<Group<'_>>) -> Value<'static, EmailProperty, EmailValue> {
|
||||
Value::Array(
|
||||
grouplist
|
||||
.into_iter()
|
||||
.map(from_mail_group)
|
||||
.collect::<Vec<Value<'static, EmailProperty, EmailValue>>>(),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ resolver = "2"
|
|||
[dependencies]
|
||||
utils = { path = "../utils" }
|
||||
trc = { path = "../trc" }
|
||||
jmap-tools = { path = "/Users/me/code/jmap-tool" }
|
||||
jmap-tools = { version = "0.1" }
|
||||
hashify = "0.2"
|
||||
serde = { version = "1.0", features = ["derive"]}
|
||||
rkyv = { version = "0.8.10", features = ["little_endian"] }
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use std::fmt::Display;
|
||||
use std::{fmt::Display, str::FromStr};
|
||||
|
||||
use jmap_tools::{Element, Property, Value};
|
||||
|
||||
|
|
@ -226,6 +226,14 @@ impl From<Keyword> for Vec<u8> {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromStr for Keyword {
|
||||
type Err = ();
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(Keyword::parse(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl ArchivedKeyword {
|
||||
pub fn id(&self) -> Result<u32, &str> {
|
||||
match self {
|
||||
|
|
|
|||
|
|
@ -69,6 +69,12 @@ impl<T: BitmapItem> Bitmap<T> {
|
|||
self.bitmap |= 1 << item.into();
|
||||
}
|
||||
|
||||
pub fn insert_many(&mut self, items: impl IntoIterator<Item = T>) {
|
||||
for item in items.into_iter() {
|
||||
self.insert(item);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn with_item(mut self, item: T) -> Self {
|
||||
self.insert(item);
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@ pub async fn test(params: &mut JMAPTest) {
|
|||
|
||||
// Jane grants Inbox ReadItems access to John
|
||||
jane_client
|
||||
.mailbox_update_acl(&inbox_id, "jdoe@example.com", [ACL::ReadItems])
|
||||
.mailbox_update_acl(&inbox_id, &john_id.to_string(), [ACL::ReadItems])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
|
@ -256,15 +256,9 @@ pub async fn test(params: &mut JMAPTest) {
|
|||
.await,
|
||||
);
|
||||
|
||||
// John only has ReadItems access to Inbox but no Read access
|
||||
assert_forbidden(
|
||||
john_client
|
||||
.set_default_account_id(jane_id.to_string())
|
||||
.mailbox_get(&inbox_id, [mailbox::Property::MyRights].into())
|
||||
.await,
|
||||
);
|
||||
// John only has ReadItems access to Inbox
|
||||
jane_client
|
||||
.mailbox_update_acl(&inbox_id, "jdoe@example.com", [ACL::Read, ACL::ReadItems])
|
||||
.mailbox_update_acl(&inbox_id, &john_id.to_string(), [ACL::ReadItems])
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
|
|
@ -331,8 +325,8 @@ pub async fn test(params: &mut JMAPTest) {
|
|||
jane_client
|
||||
.mailbox_update_acl(
|
||||
&inbox_id,
|
||||
"jdoe@example.com",
|
||||
[ACL::Read, ACL::ReadItems, ACL::AddItems],
|
||||
&john_id.to_string(),
|
||||
[ACL::ReadItems, ACL::AddItems],
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
|
@ -396,8 +390,8 @@ pub async fn test(params: &mut JMAPTest) {
|
|||
jane_client
|
||||
.mailbox_update_acl(
|
||||
&inbox_id,
|
||||
"jdoe@example.com",
|
||||
[ACL::Read, ACL::ReadItems, ACL::AddItems, ACL::RemoveItems],
|
||||
&john_id.to_string(),
|
||||
[ACL::ReadItems, ACL::AddItems, ACL::RemoveItems],
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
|
@ -417,13 +411,12 @@ pub async fn test(params: &mut JMAPTest) {
|
|||
jane_client
|
||||
.mailbox_update_acl(
|
||||
&inbox_id,
|
||||
"jdoe@example.com",
|
||||
&john_id.to_string(),
|
||||
[
|
||||
ACL::Read,
|
||||
ACL::ReadItems,
|
||||
ACL::AddItems,
|
||||
ACL::RemoveItems,
|
||||
ACL::ModifyItems,
|
||||
ACL::SetKeywords,
|
||||
],
|
||||
)
|
||||
.await
|
||||
|
|
@ -449,13 +442,12 @@ pub async fn test(params: &mut JMAPTest) {
|
|||
jane_client
|
||||
.mailbox_update_acl(
|
||||
&inbox_id,
|
||||
"jdoe@example.com",
|
||||
&john_id.to_string(),
|
||||
[
|
||||
ACL::Read,
|
||||
ACL::ReadItems,
|
||||
ACL::AddItems,
|
||||
ACL::RemoveItems,
|
||||
ACL::ModifyItems,
|
||||
ACL::SetKeywords,
|
||||
ACL::CreateChild,
|
||||
],
|
||||
)
|
||||
|
|
@ -478,8 +470,8 @@ pub async fn test(params: &mut JMAPTest) {
|
|||
jane_client
|
||||
.mailbox_update_acl(
|
||||
&mailbox_id,
|
||||
"jdoe@example.com",
|
||||
[ACL::Read, ACL::ReadItems, ACL::Modify],
|
||||
&john_id.to_string(),
|
||||
[ACL::ReadItems, ACL::Rename],
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
|
@ -499,8 +491,8 @@ pub async fn test(params: &mut JMAPTest) {
|
|||
jane_client
|
||||
.mailbox_update_acl(
|
||||
&mailbox_id,
|
||||
"jdoe@example.com",
|
||||
[ACL::Read, ACL::ReadItems, ACL::Modify, ACL::AddItems],
|
||||
&john_id.to_string(),
|
||||
[ACL::ReadItems, ACL::Rename, ACL::AddItems],
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
|
@ -520,14 +512,8 @@ pub async fn test(params: &mut JMAPTest) {
|
|||
jane_client
|
||||
.mailbox_update_acl(
|
||||
&mailbox_id,
|
||||
"jdoe@example.com",
|
||||
[
|
||||
ACL::Read,
|
||||
ACL::ReadItems,
|
||||
ACL::Modify,
|
||||
ACL::AddItems,
|
||||
ACL::Delete,
|
||||
],
|
||||
&john_id.to_string(),
|
||||
[ACL::ReadItems, ACL::Rename, ACL::AddItems, ACL::Delete],
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
|
@ -540,11 +526,10 @@ pub async fn test(params: &mut JMAPTest) {
|
|||
jane_client
|
||||
.mailbox_update_acl(
|
||||
&mailbox_id,
|
||||
"jdoe@example.com",
|
||||
&john_id.to_string(),
|
||||
[
|
||||
ACL::Read,
|
||||
ACL::ReadItems,
|
||||
ACL::Modify,
|
||||
ACL::Rename,
|
||||
ACL::AddItems,
|
||||
ACL::Delete,
|
||||
ACL::RemoveItems,
|
||||
|
|
@ -562,7 +547,7 @@ pub async fn test(params: &mut JMAPTest) {
|
|||
assert_forbidden(
|
||||
john_client
|
||||
.set_default_account_id(jane_id.to_string())
|
||||
.mailbox_update_acl(&inbox_id, "bill@example.com", [ACL::Read, ACL::ReadItems])
|
||||
.mailbox_update_acl(&inbox_id, &bill_id.to_string(), [ACL::ReadItems])
|
||||
.await,
|
||||
);
|
||||
assert_forbidden(
|
||||
|
|
@ -574,15 +559,14 @@ pub async fn test(params: &mut JMAPTest) {
|
|||
jane_client
|
||||
.mailbox_update_acl(
|
||||
&inbox_id,
|
||||
"jdoe@example.com",
|
||||
&john_id.to_string(),
|
||||
[
|
||||
ACL::Read,
|
||||
ACL::ReadItems,
|
||||
ACL::AddItems,
|
||||
ACL::RemoveItems,
|
||||
ACL::ModifyItems,
|
||||
ACL::SetKeywords,
|
||||
ACL::CreateChild,
|
||||
ACL::Modify,
|
||||
ACL::Rename,
|
||||
ACL::Administer,
|
||||
],
|
||||
)
|
||||
|
|
@ -602,14 +586,15 @@ pub async fn test(params: &mut JMAPTest) {
|
|||
ACL::ReadItems,
|
||||
ACL::AddItems,
|
||||
ACL::RemoveItems,
|
||||
ACL::ModifyItems,
|
||||
ACL::SetSeen,
|
||||
ACL::SetKeywords,
|
||||
ACL::CreateChild,
|
||||
ACL::Modify
|
||||
ACL::Rename
|
||||
]
|
||||
);
|
||||
john_client
|
||||
.set_default_account_id(jane_id.to_string())
|
||||
.mailbox_update_acl(&inbox_id, "bill@example.com", [ACL::Read, ACL::ReadItems])
|
||||
.mailbox_update_acl(&inbox_id, &bill_id.to_string(), [ACL::ReadItems])
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
|
|
@ -630,7 +615,7 @@ pub async fn test(params: &mut JMAPTest) {
|
|||
|
||||
// Revoke all access to John
|
||||
jane_client
|
||||
.mailbox_update_acl(&inbox_id, "jdoe@example.com", [])
|
||||
.mailbox_update_acl(&inbox_id, &john_id.to_string(), [])
|
||||
.await
|
||||
.unwrap();
|
||||
assert_forbidden(
|
||||
|
|
|
|||
|
|
@ -175,7 +175,8 @@ pub async fn test(params: &mut JMAPTest) {
|
|||
})
|
||||
.unwrap_or_default(),
|
||||
expected,
|
||||
"Pointer {pointer:?} Response: {response:?}",
|
||||
"Pointer {pointer:?} Response: {}",
|
||||
serde_json::to_string_pretty(&response).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ async fn jmap_tests() {
|
|||
vacation_response::test(&mut params).await;
|
||||
email_submission::test(&mut params).await;
|
||||
websocket::test(&mut params).await;
|
||||
quota::test(&mut params).await;
|
||||
quota::test(&mut params).await;*
|
||||
crypto::test(&mut params).await;
|
||||
blob::test(&mut params).await;*/
|
||||
permissions::test(¶ms).await;
|
||||
|
|
@ -991,6 +991,7 @@ type = "console"
|
|||
level = "{LEVEL}"
|
||||
multiline = false
|
||||
ansi = true
|
||||
#disabled-events = ["network.*", "telemetry.webhook-error"]
|
||||
disabled-events = ["network.*", "telemetry.webhook-error", "http.request-body"]
|
||||
|
||||
[webhook."test"]
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue