Test fixes - part 2

This commit is contained in:
mdecimus 2024-09-17 19:33:31 +02:00
parent 1e08e56672
commit 6b7dac0fcb
19 changed files with 423 additions and 129 deletions

View file

@ -267,19 +267,21 @@ impl AccessToken {
} }
pub fn permissions(&self) -> Vec<Permission> { pub fn permissions(&self) -> Vec<Permission> {
const BYTES_LEN: u32 = (std::mem::size_of::<usize>() * 8) as u32 - 1; const USIZE_BITS: usize = std::mem::size_of::<usize>() * 8;
const USIZE_MASK: u32 = USIZE_BITS as u32 - 1;
let mut permissions = Vec::new(); let mut permissions = Vec::new();
for (block_num, bytes) in self.permissions.inner().iter().enumerate() { for (block_num, bytes) in self.permissions.inner().iter().enumerate() {
let mut bytes = *bytes; let mut bytes = *bytes;
while bytes != 0 { while bytes != 0 {
let item = BYTES_LEN - bytes.leading_zeros(); let item = USIZE_MASK - bytes.leading_zeros();
bytes ^= 1 << item; bytes ^= 1 << item;
permissions.push( if let Some(permission) =
Permission::from_id((block_num * std::mem::size_of::<usize>()) + item as usize) Permission::from_id((block_num * USIZE_BITS) + item as usize)
.unwrap(), {
); permissions.push(permission);
}
} }
} }
permissions permissions

View file

@ -60,7 +60,7 @@ impl DirectoryStore for Store {
.await? .await?
{ {
if let Some(secret) = secret { if let Some(secret) = secret {
if principal.verify_secret(secret).await? { if !principal.verify_secret(secret).await? {
return Ok(None); return Ok(None);
} }
} }

View file

@ -827,7 +827,7 @@ impl ManageDirectory for Store {
PrincipalField::Quota, PrincipalField::Quota,
PrincipalValue::IntegerList(quotas), PrincipalValue::IntegerList(quotas),
) if matches!(principal.inner.typ, Type::Tenant) ) if matches!(principal.inner.typ, Type::Tenant)
&& quotas.len() <= (Type::Other as usize + 1) => && quotas.len() <= (Type::Role as usize + 1) =>
{ {
principal.inner.set(PrincipalField::Quota, quotas); principal.inner.set(PrincipalField::Quota, quotas);
} }
@ -1269,8 +1269,11 @@ impl ManageDirectory for Store {
.retain_int(change.field, |v| *v != permission); .retain_int(change.field, |v| *v != permission);
} }
_ => { (_, field, value) => {
return Err(trc::StoreEvent::NotSupported.caused_by(trc::location!())); return Err(error(
"Invalid parameter",
format!("Invalid value {:?} for {}", value, field.as_str()).into(),
));
} }
} }
} }
@ -1326,7 +1329,10 @@ impl ManageDirectory for Store {
.await .await
.caused_by(trc::location!())?; .caused_by(trc::location!())?;
if filter.is_none() && fields.iter().all(|f| matches!(f, PrincipalField::Name)) { if filter.is_none()
&& !fields.is_empty()
&& fields.iter().all(|f| matches!(f, PrincipalField::Name))
{
return Ok(PrincipalList { return Ok(PrincipalList {
total: results.len() as u64, total: results.len() as u64,
items: results items: results
@ -1350,7 +1356,7 @@ impl ManageDirectory for Store {
} }
}); });
let mut offset = limit * page; let mut offset = limit * page.saturating_sub(1);
let mut is_done = false; let mut is_done = false;
let map_principals = fields.is_empty() let map_principals = fields.is_empty()
|| fields.iter().any(|f| { || fields.iter().any(|f| {
@ -1394,7 +1400,7 @@ impl ManageDirectory for Store {
.caused_by(trc::location!())?; .caused_by(trc::location!())?;
} }
result.items.push(principal); result.items.push(principal);
is_done = result.items.len() >= limit; is_done = limit != 0 && result.items.len() >= limit;
} }
} else { } else {
offset -= 1; offset -= 1;

View file

@ -131,50 +131,77 @@ impl LdapDirectory {
} }
principal.append_str(PrincipalField::Name, account_name); principal.append_str(PrincipalField::Name, account_name);
// Obtain groups if return_member_of {
if return_member_of && principal.has_field(PrincipalField::MemberOf) { // Obtain groups
let mut member_of = Vec::new(); if principal.has_field(PrincipalField::MemberOf) {
for mut name in principal let mut member_of = Vec::new();
.take_str_array(PrincipalField::MemberOf) for mut name in principal
.unwrap_or_default() .take_str_array(PrincipalField::MemberOf)
{ .unwrap_or_default()
if name.contains('=') { {
let (rs, _res) = conn if name.contains('=') {
.search( let (rs, _res) = conn
&name, .search(
Scope::Base, &name,
"objectClass=*", Scope::Base,
&self.mappings.attr_name, "objectClass=*",
) &self.mappings.attr_name,
.await )
.map_err(|err| err.into_error().caused_by(trc::location!()))? .await
.success() .map_err(|err| err.into_error().caused_by(trc::location!()))?
.map_err(|err| err.into_error().caused_by(trc::location!()))?; .success()
for entry in rs { .map_err(|err| err.into_error().caused_by(trc::location!()))?;
'outer: for (attr, value) in SearchEntry::construct(entry).attrs { for entry in rs {
if self.mappings.attr_name.contains(&attr) { 'outer: for (attr, value) in SearchEntry::construct(entry).attrs {
if let Some(group) = value.into_iter().next() { if self.mappings.attr_name.contains(&attr) {
if !group.is_empty() { if let Some(group) = value.into_iter().next() {
name = group; if !group.is_empty() {
break 'outer; name = group;
break 'outer;
}
} }
} }
} }
} }
} }
member_of.push(
self.data_store
.get_or_create_principal_id(&name, Type::Group)
.await
.caused_by(trc::location!())?,
);
} }
member_of.push( // Map ids
self.data_store principal.set(PrincipalField::MemberOf, member_of);
.get_or_create_principal_id(&name, Type::Group)
.await
.caused_by(trc::location!())?,
);
} }
// Map ids // Obtain roles
principal.set(PrincipalField::MemberOf, member_of); let mut did_role_cleanup = false;
} else { for member in self
.data_store
.get_member_of(principal.id)
.await
.caused_by(trc::location!())?
{
match member.typ {
Type::List => {
principal.append_int(PrincipalField::Lists, member.principal_id);
}
Type::Role => {
if !did_role_cleanup {
principal.remove(PrincipalField::Roles);
did_role_cleanup = true;
}
principal.append_int(PrincipalField::Roles, member.principal_id);
}
_ => {
principal.append_int(PrincipalField::MemberOf, member.principal_id);
}
}
}
} else if principal.has_field(PrincipalField::MemberOf) {
principal.remove(PrincipalField::MemberOf); principal.remove(PrincipalField::MemberOf);
} }

View file

@ -147,6 +147,8 @@ impl MemoryDirectory {
{ {
principal.set(PrincipalField::Quota, quota); principal.set(PrincipalField::Quota, quota);
} }
directory.principals.push(principal);
} }
Some(directory) Some(directory)

View file

@ -105,22 +105,49 @@ impl SqlDirectory {
principal.set(PrincipalField::Name, account_name); principal.set(PrincipalField::Name, account_name);
// Obtain members // Obtain members
if return_member_of && !self.mappings.query_members.is_empty() { if return_member_of {
for row in self if !self.mappings.query_members.is_empty() {
.store for row in self
.query::<Rows>(&self.mappings.query_members, vec![principal.name().into()]) .store
.query::<Rows>(&self.mappings.query_members, vec![principal.name().into()])
.await
.caused_by(trc::location!())?
.rows
{
if let Some(Value::Text(account_id)) = row.values.first() {
principal.append_int(
PrincipalField::MemberOf,
self.data_store
.get_or_create_principal_id(account_id, Type::Group)
.await
.caused_by(trc::location!())?,
);
}
}
}
// Obtain roles
let mut did_role_cleanup = false;
for member in self
.data_store
.get_member_of(principal.id)
.await .await
.caused_by(trc::location!())? .caused_by(trc::location!())?
.rows
{ {
if let Some(Value::Text(account_id)) = row.values.first() { match member.typ {
principal.append_int( Type::List => {
PrincipalField::MemberOf, principal.append_int(PrincipalField::Lists, member.principal_id);
self.data_store }
.get_or_create_principal_id(account_id, Type::Group) Type::Role => {
.await if !did_role_cleanup {
.caused_by(trc::location!())?, principal.remove(PrincipalField::Roles);
); did_role_cleanup = true;
}
principal.append_int(PrincipalField::Roles, member.principal_id);
}
_ => {
principal.append_int(PrincipalField::MemberOf, member.principal_id);
}
} }
} }
} }

View file

@ -4,8 +4,184 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/ */
use crate::Permission;
pub mod cache; pub mod cache;
pub mod config; pub mod config;
pub mod dispatch; pub mod dispatch;
pub mod principal; pub mod principal;
pub mod secret; pub mod secret;
impl Permission {
pub fn description(&self) -> &'static str {
match self {
Permission::Impersonate => "Allows acting on behalf of another user",
Permission::UnlimitedRequests => "Removes request limits or quotas",
Permission::UnlimitedUploads => "Removes upload size or frequency limits",
Permission::DeleteSystemFolders => "Allows deletion of critical system folders",
Permission::MessageQueueList => "View message queue",
Permission::MessageQueueGet => "Retrieve specific messages from the queue",
Permission::MessageQueueUpdate => "Modify queued messages",
Permission::MessageQueueDelete => "Remove messages from the queue",
Permission::OutgoingReportList => "View reports for outgoing emails",
Permission::OutgoingReportGet => "Retrieve specific outgoing email reports",
Permission::OutgoingReportDelete => "Remove outgoing email reports",
Permission::IncomingReportList => "View reports for incoming emails",
Permission::IncomingReportGet => "Retrieve specific incoming email reports",
Permission::IncomingReportDelete => "Remove incoming email reports",
Permission::SettingsList => "View system settings",
Permission::SettingsUpdate => "Modify system settings",
Permission::SettingsDelete => "Remove system settings",
Permission::SettingsReload => "Refresh system settings",
Permission::IndividualList => "View list of individual users",
Permission::IndividualGet => "Retrieve specific user information",
Permission::IndividualUpdate => "Modify user information",
Permission::IndividualDelete => "Remove user accounts",
Permission::IndividualCreate => "Add new user accounts",
Permission::GroupList => "View list of user groups",
Permission::GroupGet => "Retrieve specific group information",
Permission::GroupUpdate => "Modify group information",
Permission::GroupDelete => "Remove user groups",
Permission::GroupCreate => "Add new user groups",
Permission::DomainList => "View list of email domains",
Permission::DomainGet => "Retrieve specific domain information",
Permission::DomainCreate => "Add new email domains",
Permission::DomainUpdate => "Modify domain information",
Permission::DomainDelete => "Remove email domains",
Permission::TenantList => "View list of tenants (in multi-tenant setup)",
Permission::TenantGet => "Retrieve specific tenant information",
Permission::TenantCreate => "Add new tenants",
Permission::TenantUpdate => "Modify tenant information",
Permission::TenantDelete => "Remove tenants",
Permission::MailingListList => "View list of mailing lists",
Permission::MailingListGet => "Retrieve specific mailing list information",
Permission::MailingListCreate => "Create new mailing lists",
Permission::MailingListUpdate => "Modify mailing list information",
Permission::MailingListDelete => "Remove mailing lists",
Permission::RoleList => "View list of roles",
Permission::RoleGet => "Retrieve specific role information",
Permission::RoleCreate => "Create new roles",
Permission::RoleUpdate => "Modify role information",
Permission::RoleDelete => "Remove roles",
Permission::PrincipalList => "View list of principals (users or system entities)",
Permission::PrincipalGet => "Retrieve specific principal information",
Permission::PrincipalCreate => "Create new principals",
Permission::PrincipalUpdate => "Modify principal information",
Permission::PrincipalDelete => "Remove principals",
Permission::BlobFetch => "Retrieve binary large objects",
Permission::PurgeBlobStore => "Clear the blob storage",
Permission::PurgeDataStore => "Clear the data storage",
Permission::PurgeLookupStore => "Clear the lookup storage",
Permission::PurgeAccount => "Completely remove an account and all associated data",
Permission::Undelete => "Restore deleted items",
Permission::DkimSignatureCreate => "Create DKIM signatures for email authentication",
Permission::DkimSignatureGet => "Retrieve DKIM signature information",
Permission::UpdateSpamFilter => "Modify spam filter settings",
Permission::UpdateWebadmin => "Modify web admin interface settings",
Permission::LogsView => "Access system logs",
Permission::SieveRun => "Execute Sieve scripts for email filtering",
Permission::Restart => "Restart the email server",
Permission::TracingList => "View list of system traces",
Permission::TracingGet => "Retrieve specific trace information",
Permission::TracingLive => "View real-time system traces",
Permission::MetricsList => "View list of system metrics",
Permission::MetricsLive => "View real-time system metrics",
Permission::Authenticate => "Perform authentication",
Permission::AuthenticateOauth => "Perform OAuth authentication",
Permission::EmailSend => "Send emails",
Permission::EmailReceive => "Receive emails",
Permission::ManageEncryption => "Handle encryption settings and operations",
Permission::ManagePasswords => "Manage user passwords",
Permission::JmapEmailGet => "Retrieve emails via JMAP",
Permission::JmapMailboxGet => "Retrieve mailboxes via JMAP",
Permission::JmapThreadGet => "Retrieve email threads via JMAP",
Permission::JmapIdentityGet => "Retrieve user identities via JMAP",
Permission::JmapEmailSubmissionGet => "Retrieve email submission info via JMAP",
Permission::JmapPushSubscriptionGet => "Retrieve push subscriptions via JMAP",
Permission::JmapSieveScriptGet => "Retrieve Sieve scripts via JMAP",
Permission::JmapVacationResponseGet => "Retrieve vacation responses via JMAP",
Permission::JmapPrincipalGet => "Retrieve principal information via JMAP",
Permission::JmapQuotaGet => "Retrieve quota information via JMAP",
Permission::JmapBlobGet => "Retrieve blobs via JMAP",
Permission::JmapEmailSet => "Modify emails via JMAP",
Permission::JmapMailboxSet => "Modify mailboxes via JMAP",
Permission::JmapIdentitySet => "Modify user identities via JMAP",
Permission::JmapEmailSubmissionSet => "Modify email submission settings via JMAP",
Permission::JmapPushSubscriptionSet => "Modify push subscriptions via JMAP",
Permission::JmapSieveScriptSet => "Modify Sieve scripts via JMAP",
Permission::JmapVacationResponseSet => "Modify vacation responses via JMAP",
Permission::JmapEmailChanges => "Track email changes via JMAP",
Permission::JmapMailboxChanges => "Track mailbox changes via JMAP",
Permission::JmapThreadChanges => "Track thread changes via JMAP",
Permission::JmapIdentityChanges => "Track identity changes via JMAP",
Permission::JmapEmailSubmissionChanges => "Track email submission changes via JMAP",
Permission::JmapQuotaChanges => "Track quota changes via JMAP",
Permission::JmapEmailCopy => "Copy emails via JMAP",
Permission::JmapBlobCopy => "Copy blobs via JMAP",
Permission::JmapEmailImport => "Import emails via JMAP",
Permission::JmapEmailParse => "Parse emails via JMAP",
Permission::JmapEmailQueryChanges => "Track email query changes via JMAP",
Permission::JmapMailboxQueryChanges => "Track mailbox query changes via JMAP",
Permission::JmapEmailSubmissionQueryChanges => {
"Track email submission query changes via JMAP"
}
Permission::JmapSieveScriptQueryChanges => "Track Sieve script query changes via JMAP",
Permission::JmapPrincipalQueryChanges => "Track principal query changes via JMAP",
Permission::JmapQuotaQueryChanges => "Track quota query changes via JMAP",
Permission::JmapEmailQuery => "Perform email queries via JMAP",
Permission::JmapMailboxQuery => "Perform mailbox queries via JMAP",
Permission::JmapEmailSubmissionQuery => "Perform email submission queries via JMAP",
Permission::JmapSieveScriptQuery => "Perform Sieve script queries via JMAP",
Permission::JmapPrincipalQuery => "Perform principal queries via JMAP",
Permission::JmapQuotaQuery => "Perform quota queries via JMAP",
Permission::JmapSearchSnippet => "Retrieve search snippets via JMAP",
Permission::JmapSieveScriptValidate => "Validate Sieve scripts via JMAP",
Permission::JmapBlobLookup => "Look up blobs via JMAP",
Permission::JmapBlobUpload => "Upload blobs via JMAP",
Permission::JmapEcho => "Perform JMAP echo requests",
Permission::ImapAuthenticate => "Authenticate via IMAP",
Permission::ImapAclGet => "Retrieve ACLs via IMAP",
Permission::ImapAclSet => "Set ACLs via IMAP",
Permission::ImapMyRights => "Retrieve own rights via IMAP",
Permission::ImapListRights => "List rights via IMAP",
Permission::ImapAppend => "Append messages via IMAP",
Permission::ImapCapability => "Retrieve server capabilities via IMAP",
Permission::ImapId => "Retrieve server ID via IMAP",
Permission::ImapCopy => "Copy messages via IMAP",
Permission::ImapMove => "Move messages via IMAP",
Permission::ImapCreate => "Create mailboxes via IMAP",
Permission::ImapDelete => "Delete mailboxes or messages via IMAP",
Permission::ImapEnable => "Enable IMAP extensions",
Permission::ImapExpunge => "Expunge deleted messages via IMAP",
Permission::ImapFetch => "Fetch messages or metadata via IMAP",
Permission::ImapIdle => "Use IMAP IDLE command",
Permission::ImapList => "List mailboxes via IMAP",
Permission::ImapLsub => "List subscribed mailboxes via IMAP",
Permission::ImapNamespace => "Retrieve namespaces via IMAP",
Permission::ImapRename => "Rename mailboxes via IMAP",
Permission::ImapSearch => "Search messages via IMAP",
Permission::ImapSort => "Sort messages via IMAP",
Permission::ImapSelect => "Select mailboxes via IMAP",
Permission::ImapExamine => "Examine mailboxes via IMAP",
Permission::ImapStatus => "Retrieve mailbox status via IMAP",
Permission::ImapStore => "Modify message flags via IMAP",
Permission::ImapSubscribe => "Subscribe to mailboxes via IMAP",
Permission::ImapThread => "Thread messages via IMAP",
Permission::Pop3Authenticate => "Authenticate via POP3",
Permission::Pop3List => "List messages via POP3",
Permission::Pop3Uidl => "Retrieve unique IDs via POP3",
Permission::Pop3Stat => "Retrieve mailbox statistics via POP3",
Permission::Pop3Retr => "Retrieve messages via POP3",
Permission::Pop3Dele => "Mark messages for deletion via POP3",
Permission::SieveAuthenticate => "Authenticate for Sieve script management",
Permission::SieveListScripts => "List Sieve scripts",
Permission::SieveSetActive => "Set active Sieve script",
Permission::SieveGetScript => "Retrieve Sieve scripts",
Permission::SievePutScript => "Upload Sieve scripts",
Permission::SieveDeleteScript => "Delete Sieve scripts",
Permission::SieveRenameScript => "Rename Sieve scripts",
Permission::SieveCheckScript => "Validate Sieve scripts",
Permission::SieveHaveSpace => "Check available space for Sieve scripts",
}
}
}

View file

@ -610,7 +610,7 @@ impl<'de> serde::Deserialize<'de> for PrincipalValue {
type Value = PrincipalValue; type Value = PrincipalValue;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("an optional u64 or a vector of u64") formatter.write_str("an optional values or a sequence of values")
} }
fn visit_none<E>(self) -> Result<Self::Value, E> fn visit_none<E>(self) -> Result<Self::Value, E>
@ -671,7 +671,7 @@ impl<'de> serde::Deserialize<'de> for PrincipalValue {
} }
} }
deserializer.deserialize_map(PrincipalValueVisitor) deserializer.deserialize_any(PrincipalValueVisitor)
} }
} }
@ -748,6 +748,45 @@ impl<'de> serde::Deserialize<'de> for Principal {
} }
} }
#[derive(Debug)]
enum StringOrU64 {
String(String),
U64(u64),
}
impl<'de> serde::Deserialize<'de> for StringOrU64 {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct StringOrU64Visitor;
impl<'de> Visitor<'de> for StringOrU64Visitor {
type Value = StringOrU64;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a string or u64")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(StringOrU64::String(value.to_string()))
}
fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(StringOrU64::U64(value))
}
}
deserializer.deserialize_any(StringOrU64Visitor)
}
}
impl Permission { impl Permission {
pub const fn is_user_permission(&self) -> bool { pub const fn is_user_permission(&self) -> bool {
matches!( matches!(
@ -898,42 +937,3 @@ impl Permission {
) || self.is_user_permission() ) || self.is_user_permission()
} }
} }
#[derive(Debug)]
enum StringOrU64 {
String(String),
U64(u64),
}
impl<'de> serde::Deserialize<'de> for StringOrU64 {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct StringOrU64Visitor;
impl<'de> Visitor<'de> for StringOrU64Visitor {
type Value = StringOrU64;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a string or u64")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(StringOrU64::String(value.to_string()))
}
fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(StringOrU64::U64(value))
}
}
deserializer.deserialize_any(StringOrU64Visitor)
}
}

View file

@ -351,10 +351,6 @@ impl<T: SessionStream> SessionData<T> {
.shared_accounts(Collection::Mailbox) .shared_accounts(Collection::Mailbox)
.copied() .copied()
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let c = println!(
"{} has_access_to: {:?}",
access_token.primary_id, has_access_to
);
for account in mailboxes.drain(..) { for account in mailboxes.drain(..) {
if access_token.is_primary_id(account.account_id) if access_token.is_primary_id(account.account_id)
|| has_access_to.contains(&account.account_id) || has_access_to.contains(&account.account_id)

View file

@ -89,7 +89,9 @@ impl<T: SessionStream> SessionData<T> {
// Obtain quota // Obtain quota
let resource_token = self let resource_token = self
.get_access_token() .jmap
.core
.get_cached_access_token(mailbox.account_id)
.await .await
.imap_ctx(&arguments.tag, trc::location!())? .imap_ctx(&arguments.tag, trc::location!())?
.as_resource_token(); .as_resource_token();

View file

@ -68,19 +68,16 @@ impl<T: SessionStream> SessionData<T> {
op_start: Instant, op_start: Instant,
) -> trc::Result<Vec<u8>> { ) -> trc::Result<Vec<u8>> {
// Resync messages if needed // Resync messages if needed
let c = println!("Checking mailbox acl 1 {:?}", mailbox.state.lock());
let account_id = mailbox.id.account_id; let account_id = mailbox.id.account_id;
self.synchronize_messages(&mailbox) self.synchronize_messages(&mailbox)
.await .await
.imap_ctx(&arguments.tag, trc::location!())?; .imap_ctx(&arguments.tag, trc::location!())?;
// Convert IMAP ids to JMAP ids. // Convert IMAP ids to JMAP ids.
let c = println!("Checking mailbox acl 2 {:?}", mailbox.state.lock());
let mut ids = mailbox let mut ids = mailbox
.sequence_to_ids(&arguments.sequence_set, is_uid) .sequence_to_ids(&arguments.sequence_set, is_uid)
.await .await
.imap_ctx(&arguments.tag, trc::location!())?; .imap_ctx(&arguments.tag, trc::location!())?;
let c = println!("Checking mailbox acl3 {:?}", arguments.sequence_set);
if ids.is_empty() { if ids.is_empty() {
return Ok(StatusResponse::completed(Command::Store(is_uid)) return Ok(StatusResponse::completed(Command::Store(is_uid))
.with_tag(arguments.tag) .with_tag(arguments.tag)
@ -88,7 +85,6 @@ impl<T: SessionStream> SessionData<T> {
} }
// Verify that the user can modify messages in this mailbox. // Verify that the user can modify messages in this mailbox.
let c = println!("Checking mailbox acl4");
if !self if !self
.check_mailbox_acl( .check_mailbox_acl(
mailbox.id.account_id, mailbox.id.account_id,

View file

@ -302,7 +302,9 @@ impl JMAP {
} }
#[cfg(not(feature = "test_mode"))] #[cfg(not(feature = "test_mode"))]
if [INBOX_ID, TRASH_ID, JUNK_ID].contains(&document_id) && !access_token.is_super_user() { if [INBOX_ID, TRASH_ID, JUNK_ID].contains(&document_id)
&& !access_token.has_permission(Permission::DeleteSystemFolders)
{
return Ok(Err(SetError::forbidden().with_description( return Ok(Err(SetError::forbidden().with_description(
"You are not allowed to delete Inbox, Junk or Trash folders.", "You are not allowed to delete Inbox, Junk or Trash folders.",
))); )));

View file

@ -449,6 +449,7 @@ async fn internal_directory() {
quota: 1024, quota: 1024,
typ: Type::Individual, typ: Type::Individual,
member_of: vec!["list".to_string(), "sales".to_string()], member_of: vec!["list".to_string(), "sales".to_string()],
..Default::default()
} }
); );
assert_eq!(store.get_principal_id("john").await.unwrap(), None); assert_eq!(store.get_principal_id("john").await.unwrap(), None);
@ -530,7 +531,14 @@ async fn internal_directory() {
// List accounts // List accounts
assert_eq!( assert_eq!(
store store
.list_principals(None, None, &[], &[], 0, 0) .list_principals(
None,
None,
&[Type::Individual, Type::Group, Type::List],
&[],
0,
0
)
.await .await
.unwrap() .unwrap()
.items .items
@ -633,7 +641,14 @@ async fn internal_directory() {
assert!(!store.rcpt("john.doe@example.org").await.unwrap()); assert!(!store.rcpt("john.doe@example.org").await.unwrap());
assert_eq!( assert_eq!(
store store
.list_principals(None, None, &[], &[], 0, 0) .list_principals(
None,
None,
&[Type::Individual, Type::Group, Type::List],
&[],
0,
0
)
.await .await
.unwrap() .unwrap()
.items .items

View file

@ -6,7 +6,7 @@
use std::fmt::Debug; use std::fmt::Debug;
use directory::{backend::internal::manage::ManageDirectory, QueryBy, Type}; use directory::{backend::internal::manage::ManageDirectory, QueryBy, Type, ROLE_USER};
use mail_send::Credentials; use mail_send::Credentials;
use crate::directory::{map_account_ids, DirectoryTest, IntoTestPrincipal, TestPrincipal}; use crate::directory::{map_account_ids, DirectoryTest, IntoTestPrincipal, TestPrincipal};
@ -57,6 +57,7 @@ async fn ldap_directory() {
"john@example.org".to_string(), "john@example.org".to_string(),
"john.doe@example.org".to_string() "john.doe@example.org".to_string()
], ],
roles: vec![ROLE_USER.to_string()],
..Default::default() ..Default::default()
} }
.into_sorted() .into_sorted()
@ -85,6 +86,7 @@ async fn ldap_directory() {
typ: Type::Individual, typ: Type::Individual,
quota: 500000, quota: 500000,
emails: vec!["bill@example.org".to_string(),], emails: vec!["bill@example.org".to_string(),],
roles: vec![ROLE_USER.to_string()],
..Default::default() ..Default::default()
} }
.into_sorted() .into_sorted()
@ -122,6 +124,7 @@ async fn ldap_directory() {
.map(|v| v.to_string()) .map(|v| v.to_string())
.collect(), .collect(),
emails: vec!["jane@example.org".to_string(),], emails: vec!["jane@example.org".to_string(),],
roles: vec![ROLE_USER.to_string()],
..Default::default() ..Default::default()
} }
.into_sorted() .into_sorted()
@ -140,6 +143,7 @@ async fn ldap_directory() {
name: "sales".to_string(), name: "sales".to_string(),
description: "sales".to_string().into(), description: "sales".to_string().into(),
typ: Type::Group, typ: Type::Group,
roles: vec![ROLE_USER.to_string()],
..Default::default() ..Default::default()
} }
); );

View file

@ -266,6 +266,7 @@ pub struct TestPrincipal {
pub secrets: Vec<String>, pub secrets: Vec<String>,
pub emails: Vec<String>, pub emails: Vec<String>,
pub member_of: Vec<String>, pub member_of: Vec<String>,
pub roles: Vec<String>,
pub description: Option<String>, pub description: Option<String>,
} }
@ -457,10 +458,9 @@ impl From<Principal> for TestPrincipal {
member_of: value member_of: value
.take_str_array(PrincipalField::MemberOf) .take_str_array(PrincipalField::MemberOf)
.unwrap_or_default(), .unwrap_or_default(),
/*member_of: value roles: value
.iter_int(PrincipalField::MemberOf) .take_str_array(PrincipalField::Roles)
.map(|v| v as u32) .unwrap_or_default(),
.collect(),*/
description: value.take_str(PrincipalField::Description), description: value.take_str(PrincipalField::Description),
} }
} }

View file

@ -4,7 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/ */
use directory::{backend::internal::manage::ManageDirectory, QueryBy, Type}; use directory::{backend::internal::manage::ManageDirectory, QueryBy, Type, ROLE_ADMIN, ROLE_USER};
use mail_send::Credentials; use mail_send::Credentials;
use store::{LookupStore, Store}; use store::{LookupStore, Store};
@ -39,6 +39,9 @@ async fn sql_directory() {
store.create_test_directory().await; store.create_test_directory().await;
// Create test users // Create test users
store
.create_test_user("admin", "very_secret", "Administrator")
.await;
store.create_test_user("john", "12345", "John Doe").await; store.create_test_user("john", "12345", "John Doe").await;
store.create_test_user("jane", "abcde", "Jane Doe").await; store.create_test_user("jane", "abcde", "Jane Doe").await;
store store
@ -128,6 +131,7 @@ async fn sql_directory() {
"jdoe@example.org".to_string(), "jdoe@example.org".to_string(),
"john.doe@example.org".to_string() "john.doe@example.org".to_string()
], ],
roles: vec![ROLE_USER.to_string()],
..Default::default() ..Default::default()
} }
); );
@ -154,6 +158,30 @@ async fn sql_directory() {
typ: Type::Individual, typ: Type::Individual,
quota: 500000, quota: 500000,
emails: vec!["bill@example.org".to_string(),], emails: vec!["bill@example.org".to_string(),],
roles: vec![ROLE_USER.to_string()],
..Default::default()
}
);
assert_eq!(
handle
.query(
QueryBy::Credentials(&Credentials::Plain {
username: "admin".to_string(),
secret: "very_secret".to_string()
}),
true
)
.await
.unwrap()
.unwrap()
.into_test(),
TestPrincipal {
id: base_store.get_principal_id("admin").await.unwrap().unwrap(),
name: "admin".to_string(),
description: "Administrator".to_string().into(),
secrets: vec!["very_secret".to_string()],
typ: Type::Individual,
roles: vec![ROLE_ADMIN.to_string()],
..Default::default() ..Default::default()
} }
); );
@ -189,6 +217,7 @@ async fn sql_directory() {
.map(|v| v.to_string()) .map(|v| v.to_string())
.collect(), .collect(),
emails: vec!["jane@example.org".to_string(),], emails: vec!["jane@example.org".to_string(),],
roles: vec![ROLE_USER.to_string()],
..Default::default() ..Default::default()
} }
); );
@ -206,6 +235,7 @@ async fn sql_directory() {
name: "sales".to_string(), name: "sales".to_string(),
description: "Sales Team".to_string().into(), description: "Sales Team".to_string().into(),
typ: Type::Group, typ: Type::Group,
roles: vec![ROLE_USER.to_string()],
..Default::default() ..Default::default()
} }
); );

View file

@ -166,7 +166,6 @@ pub async fn test(mut imap_john: &mut ImapConnection, _imap_check: &mut ImapConn
.await; .await;
imap.assert_read(Type::Tagged, ResponseType::Ok).await; imap.assert_read(Type::Tagged, ResponseType::Ok).await;
} }
let c = println!("----cococ");
imap_john.send("UID STORE 1 +FLAGS (\\Deleted)").await; imap_john.send("UID STORE 1 +FLAGS (\\Deleted)").await;
imap_john.assert_read(Type::Tagged, ResponseType::No).await; imap_john.assert_read(Type::Tagged, ResponseType::No).await;

View file

@ -9,10 +9,13 @@ use common::Core;
use store::Stores; use store::Stores;
use utils::config::Config; use utils::config::Config;
use crate::smtp::{ use crate::{
build_smtp, smtp::{
session::{TestSession, VerifyResponse}, build_smtp,
TempDir, session::{TestSession, VerifyResponse},
TempDir,
},
AssertConfig,
}; };
use smtp::core::{Inner, Session, State}; use smtp::core::{Inner, Session, State};
@ -22,6 +25,7 @@ data = "sqlite"
lookup = "sqlite" lookup = "sqlite"
blob = "sqlite" blob = "sqlite"
fts = "sqlite" fts = "sqlite"
directory = "local"
[store."sqlite"] [store."sqlite"]
type = "sqlite" type = "sqlite"
@ -74,6 +78,7 @@ async fn auth() {
let mut config = Config::new(tmp_dir.update_config(CONFIG)).unwrap(); let mut config = Config::new(tmp_dir.update_config(CONFIG)).unwrap();
let stores = Stores::parse_all(&mut config).await; let stores = Stores::parse_all(&mut config).await;
let core = Core::parse(&mut config, stores, Default::default()).await; let core = Core::parse(&mut config, stores, Default::default()).await;
config.assert_no_errors();
// EHLO should not advertise plain text auth without TLS // EHLO should not advertise plain text auth without TLS
let mut session = Session::test(build_smtp(core, Inner::default())); let mut session = Session::test(build_smtp(core, Inner::default()));

View file

@ -11,10 +11,13 @@ use utils::config::Config;
use smtp::core::{Inner, Session}; use smtp::core::{Inner, Session};
use crate::smtp::{ use crate::{
build_smtp, smtp::{
session::{TestSession, VerifyResponse}, build_smtp,
TempDir, session::{TestSession, VerifyResponse},
TempDir,
},
AssertConfig,
}; };
const CONFIG: &str = r#" const CONFIG: &str = r#"
@ -23,6 +26,7 @@ data = "sqlite"
lookup = "sqlite" lookup = "sqlite"
blob = "sqlite" blob = "sqlite"
fts = "sqlite" fts = "sqlite"
directory = "local"
[store."sqlite"] [store."sqlite"]
type = "sqlite" type = "sqlite"
@ -72,6 +76,7 @@ async fn vrfy_expn() {
let mut config = Config::new(tmp_dir.update_config(CONFIG)).unwrap(); let mut config = Config::new(tmp_dir.update_config(CONFIG)).unwrap();
let stores = Stores::parse_all(&mut config).await; let stores = Stores::parse_all(&mut config).await;
let core = Core::parse(&mut config, stores, Default::default()).await; let core = Core::parse(&mut config, stores, Default::default()).await;
config.assert_no_errors();
// EHLO should not advertise VRFY/EXPN to 10.0.0.2 // EHLO should not advertise VRFY/EXPN to 10.0.0.2
let mut session = Session::test(build_smtp(core, Inner::default())); let mut session = Session::test(build_smtp(core, Inner::default()));