mirror of
https://github.com/stalwartlabs/mail-server.git
synced 2024-11-28 09:07:32 +00:00
Test fixes - part 2
This commit is contained in:
parent
1e08e56672
commit
6b7dac0fcb
19 changed files with 423 additions and 129 deletions
|
@ -267,19 +267,21 @@ impl AccessToken {
|
|||
}
|
||||
|
||||
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();
|
||||
|
||||
for (block_num, bytes) in self.permissions.inner().iter().enumerate() {
|
||||
let mut bytes = *bytes;
|
||||
|
||||
while bytes != 0 {
|
||||
let item = BYTES_LEN - bytes.leading_zeros();
|
||||
let item = USIZE_MASK - bytes.leading_zeros();
|
||||
bytes ^= 1 << item;
|
||||
permissions.push(
|
||||
Permission::from_id((block_num * std::mem::size_of::<usize>()) + item as usize)
|
||||
.unwrap(),
|
||||
);
|
||||
if let Some(permission) =
|
||||
Permission::from_id((block_num * USIZE_BITS) + item as usize)
|
||||
{
|
||||
permissions.push(permission);
|
||||
}
|
||||
}
|
||||
}
|
||||
permissions
|
||||
|
|
|
@ -60,7 +60,7 @@ impl DirectoryStore for Store {
|
|||
.await?
|
||||
{
|
||||
if let Some(secret) = secret {
|
||||
if principal.verify_secret(secret).await? {
|
||||
if !principal.verify_secret(secret).await? {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -827,7 +827,7 @@ impl ManageDirectory for Store {
|
|||
PrincipalField::Quota,
|
||||
PrincipalValue::IntegerList(quotas),
|
||||
) 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);
|
||||
}
|
||||
|
@ -1269,8 +1269,11 @@ impl ManageDirectory for Store {
|
|||
.retain_int(change.field, |v| *v != permission);
|
||||
}
|
||||
|
||||
_ => {
|
||||
return Err(trc::StoreEvent::NotSupported.caused_by(trc::location!()));
|
||||
(_, field, value) => {
|
||||
return Err(error(
|
||||
"Invalid parameter",
|
||||
format!("Invalid value {:?} for {}", value, field.as_str()).into(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1326,7 +1329,10 @@ impl ManageDirectory for Store {
|
|||
.await
|
||||
.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 {
|
||||
total: results.len() as u64,
|
||||
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 map_principals = fields.is_empty()
|
||||
|| fields.iter().any(|f| {
|
||||
|
@ -1394,7 +1400,7 @@ impl ManageDirectory for Store {
|
|||
.caused_by(trc::location!())?;
|
||||
}
|
||||
result.items.push(principal);
|
||||
is_done = result.items.len() >= limit;
|
||||
is_done = limit != 0 && result.items.len() >= limit;
|
||||
}
|
||||
} else {
|
||||
offset -= 1;
|
||||
|
|
|
@ -131,50 +131,77 @@ impl LdapDirectory {
|
|||
}
|
||||
principal.append_str(PrincipalField::Name, account_name);
|
||||
|
||||
// Obtain groups
|
||||
if return_member_of && principal.has_field(PrincipalField::MemberOf) {
|
||||
let mut member_of = Vec::new();
|
||||
for mut name in principal
|
||||
.take_str_array(PrincipalField::MemberOf)
|
||||
.unwrap_or_default()
|
||||
{
|
||||
if name.contains('=') {
|
||||
let (rs, _res) = conn
|
||||
.search(
|
||||
&name,
|
||||
Scope::Base,
|
||||
"objectClass=*",
|
||||
&self.mappings.attr_name,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| err.into_error().caused_by(trc::location!()))?
|
||||
.success()
|
||||
.map_err(|err| err.into_error().caused_by(trc::location!()))?;
|
||||
for entry in rs {
|
||||
'outer: for (attr, value) in SearchEntry::construct(entry).attrs {
|
||||
if self.mappings.attr_name.contains(&attr) {
|
||||
if let Some(group) = value.into_iter().next() {
|
||||
if !group.is_empty() {
|
||||
name = group;
|
||||
break 'outer;
|
||||
if return_member_of {
|
||||
// Obtain groups
|
||||
if principal.has_field(PrincipalField::MemberOf) {
|
||||
let mut member_of = Vec::new();
|
||||
for mut name in principal
|
||||
.take_str_array(PrincipalField::MemberOf)
|
||||
.unwrap_or_default()
|
||||
{
|
||||
if name.contains('=') {
|
||||
let (rs, _res) = conn
|
||||
.search(
|
||||
&name,
|
||||
Scope::Base,
|
||||
"objectClass=*",
|
||||
&self.mappings.attr_name,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| err.into_error().caused_by(trc::location!()))?
|
||||
.success()
|
||||
.map_err(|err| err.into_error().caused_by(trc::location!()))?;
|
||||
for entry in rs {
|
||||
'outer: for (attr, value) in SearchEntry::construct(entry).attrs {
|
||||
if self.mappings.attr_name.contains(&attr) {
|
||||
if let Some(group) = value.into_iter().next() {
|
||||
if !group.is_empty() {
|
||||
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(
|
||||
self.data_store
|
||||
.get_or_create_principal_id(&name, Type::Group)
|
||||
.await
|
||||
.caused_by(trc::location!())?,
|
||||
);
|
||||
// Map ids
|
||||
principal.set(PrincipalField::MemberOf, member_of);
|
||||
}
|
||||
|
||||
// Map ids
|
||||
principal.set(PrincipalField::MemberOf, member_of);
|
||||
} else {
|
||||
// Obtain roles
|
||||
let mut did_role_cleanup = false;
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -147,6 +147,8 @@ impl MemoryDirectory {
|
|||
{
|
||||
principal.set(PrincipalField::Quota, quota);
|
||||
}
|
||||
|
||||
directory.principals.push(principal);
|
||||
}
|
||||
|
||||
Some(directory)
|
||||
|
|
|
@ -105,22 +105,49 @@ impl SqlDirectory {
|
|||
principal.set(PrincipalField::Name, account_name);
|
||||
|
||||
// Obtain members
|
||||
if return_member_of && !self.mappings.query_members.is_empty() {
|
||||
for row in self
|
||||
.store
|
||||
.query::<Rows>(&self.mappings.query_members, vec![principal.name().into()])
|
||||
if return_member_of {
|
||||
if !self.mappings.query_members.is_empty() {
|
||||
for row in self
|
||||
.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
|
||||
.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!())?,
|
||||
);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,8 +4,184 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use crate::Permission;
|
||||
|
||||
pub mod cache;
|
||||
pub mod config;
|
||||
pub mod dispatch;
|
||||
pub mod principal;
|
||||
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",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -610,7 +610,7 @@ impl<'de> serde::Deserialize<'de> for PrincipalValue {
|
|||
type Value = PrincipalValue;
|
||||
|
||||
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>
|
||||
|
@ -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 {
|
||||
pub const fn is_user_permission(&self) -> bool {
|
||||
matches!(
|
||||
|
@ -898,42 +937,3 @@ impl 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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -351,10 +351,6 @@ impl<T: SessionStream> SessionData<T> {
|
|||
.shared_accounts(Collection::Mailbox)
|
||||
.copied()
|
||||
.collect::<Vec<_>>();
|
||||
let c = println!(
|
||||
"{} has_access_to: {:?}",
|
||||
access_token.primary_id, has_access_to
|
||||
);
|
||||
for account in mailboxes.drain(..) {
|
||||
if access_token.is_primary_id(account.account_id)
|
||||
|| has_access_to.contains(&account.account_id)
|
||||
|
|
|
@ -89,7 +89,9 @@ impl<T: SessionStream> SessionData<T> {
|
|||
|
||||
// Obtain quota
|
||||
let resource_token = self
|
||||
.get_access_token()
|
||||
.jmap
|
||||
.core
|
||||
.get_cached_access_token(mailbox.account_id)
|
||||
.await
|
||||
.imap_ctx(&arguments.tag, trc::location!())?
|
||||
.as_resource_token();
|
||||
|
|
|
@ -68,19 +68,16 @@ impl<T: SessionStream> SessionData<T> {
|
|||
op_start: Instant,
|
||||
) -> trc::Result<Vec<u8>> {
|
||||
// Resync messages if needed
|
||||
let c = println!("Checking mailbox acl 1 {:?}", mailbox.state.lock());
|
||||
let account_id = mailbox.id.account_id;
|
||||
self.synchronize_messages(&mailbox)
|
||||
.await
|
||||
.imap_ctx(&arguments.tag, trc::location!())?;
|
||||
|
||||
// Convert IMAP ids to JMAP ids.
|
||||
let c = println!("Checking mailbox acl 2 {:?}", mailbox.state.lock());
|
||||
let mut ids = mailbox
|
||||
.sequence_to_ids(&arguments.sequence_set, is_uid)
|
||||
.await
|
||||
.imap_ctx(&arguments.tag, trc::location!())?;
|
||||
let c = println!("Checking mailbox acl3 {:?}", arguments.sequence_set);
|
||||
if ids.is_empty() {
|
||||
return Ok(StatusResponse::completed(Command::Store(is_uid))
|
||||
.with_tag(arguments.tag)
|
||||
|
@ -88,7 +85,6 @@ impl<T: SessionStream> SessionData<T> {
|
|||
}
|
||||
|
||||
// Verify that the user can modify messages in this mailbox.
|
||||
let c = println!("Checking mailbox acl4");
|
||||
if !self
|
||||
.check_mailbox_acl(
|
||||
mailbox.id.account_id,
|
||||
|
|
|
@ -302,7 +302,9 @@ impl JMAP {
|
|||
}
|
||||
|
||||
#[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(
|
||||
"You are not allowed to delete Inbox, Junk or Trash folders.",
|
||||
)));
|
||||
|
|
|
@ -449,6 +449,7 @@ async fn internal_directory() {
|
|||
quota: 1024,
|
||||
typ: Type::Individual,
|
||||
member_of: vec!["list".to_string(), "sales".to_string()],
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
assert_eq!(store.get_principal_id("john").await.unwrap(), None);
|
||||
|
@ -530,7 +531,14 @@ async fn internal_directory() {
|
|||
// List accounts
|
||||
assert_eq!(
|
||||
store
|
||||
.list_principals(None, None, &[], &[], 0, 0)
|
||||
.list_principals(
|
||||
None,
|
||||
None,
|
||||
&[Type::Individual, Type::Group, Type::List],
|
||||
&[],
|
||||
0,
|
||||
0
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.items
|
||||
|
@ -633,7 +641,14 @@ async fn internal_directory() {
|
|||
assert!(!store.rcpt("john.doe@example.org").await.unwrap());
|
||||
assert_eq!(
|
||||
store
|
||||
.list_principals(None, None, &[], &[], 0, 0)
|
||||
.list_principals(
|
||||
None,
|
||||
None,
|
||||
&[Type::Individual, Type::Group, Type::List],
|
||||
&[],
|
||||
0,
|
||||
0
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.items
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
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 crate::directory::{map_account_ids, DirectoryTest, IntoTestPrincipal, TestPrincipal};
|
||||
|
@ -57,6 +57,7 @@ async fn ldap_directory() {
|
|||
"john@example.org".to_string(),
|
||||
"john.doe@example.org".to_string()
|
||||
],
|
||||
roles: vec![ROLE_USER.to_string()],
|
||||
..Default::default()
|
||||
}
|
||||
.into_sorted()
|
||||
|
@ -85,6 +86,7 @@ async fn ldap_directory() {
|
|||
typ: Type::Individual,
|
||||
quota: 500000,
|
||||
emails: vec!["bill@example.org".to_string(),],
|
||||
roles: vec![ROLE_USER.to_string()],
|
||||
..Default::default()
|
||||
}
|
||||
.into_sorted()
|
||||
|
@ -122,6 +124,7 @@ async fn ldap_directory() {
|
|||
.map(|v| v.to_string())
|
||||
.collect(),
|
||||
emails: vec!["jane@example.org".to_string(),],
|
||||
roles: vec![ROLE_USER.to_string()],
|
||||
..Default::default()
|
||||
}
|
||||
.into_sorted()
|
||||
|
@ -140,6 +143,7 @@ async fn ldap_directory() {
|
|||
name: "sales".to_string(),
|
||||
description: "sales".to_string().into(),
|
||||
typ: Type::Group,
|
||||
roles: vec![ROLE_USER.to_string()],
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
|
|
@ -266,6 +266,7 @@ pub struct TestPrincipal {
|
|||
pub secrets: Vec<String>,
|
||||
pub emails: Vec<String>,
|
||||
pub member_of: Vec<String>,
|
||||
pub roles: Vec<String>,
|
||||
pub description: Option<String>,
|
||||
}
|
||||
|
||||
|
@ -457,10 +458,9 @@ impl From<Principal> for TestPrincipal {
|
|||
member_of: value
|
||||
.take_str_array(PrincipalField::MemberOf)
|
||||
.unwrap_or_default(),
|
||||
/*member_of: value
|
||||
.iter_int(PrincipalField::MemberOf)
|
||||
.map(|v| v as u32)
|
||||
.collect(),*/
|
||||
roles: value
|
||||
.take_str_array(PrincipalField::Roles)
|
||||
.unwrap_or_default(),
|
||||
description: value.take_str(PrincipalField::Description),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* 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 store::{LookupStore, Store};
|
||||
|
||||
|
@ -39,6 +39,9 @@ async fn sql_directory() {
|
|||
store.create_test_directory().await;
|
||||
|
||||
// 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("jane", "abcde", "Jane Doe").await;
|
||||
store
|
||||
|
@ -128,6 +131,7 @@ async fn sql_directory() {
|
|||
"jdoe@example.org".to_string(),
|
||||
"john.doe@example.org".to_string()
|
||||
],
|
||||
roles: vec![ROLE_USER.to_string()],
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
@ -154,6 +158,30 @@ async fn sql_directory() {
|
|||
typ: Type::Individual,
|
||||
quota: 500000,
|
||||
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()
|
||||
}
|
||||
);
|
||||
|
@ -189,6 +217,7 @@ async fn sql_directory() {
|
|||
.map(|v| v.to_string())
|
||||
.collect(),
|
||||
emails: vec!["jane@example.org".to_string(),],
|
||||
roles: vec![ROLE_USER.to_string()],
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
@ -206,6 +235,7 @@ async fn sql_directory() {
|
|||
name: "sales".to_string(),
|
||||
description: "Sales Team".to_string().into(),
|
||||
typ: Type::Group,
|
||||
roles: vec![ROLE_USER.to_string()],
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
|
|
@ -166,7 +166,6 @@ pub async fn test(mut imap_john: &mut ImapConnection, _imap_check: &mut ImapConn
|
|||
.await;
|
||||
imap.assert_read(Type::Tagged, ResponseType::Ok).await;
|
||||
}
|
||||
let c = println!("----cococ");
|
||||
imap_john.send("UID STORE 1 +FLAGS (\\Deleted)").await;
|
||||
imap_john.assert_read(Type::Tagged, ResponseType::No).await;
|
||||
|
||||
|
|
|
@ -9,10 +9,13 @@ use common::Core;
|
|||
use store::Stores;
|
||||
use utils::config::Config;
|
||||
|
||||
use crate::smtp::{
|
||||
build_smtp,
|
||||
session::{TestSession, VerifyResponse},
|
||||
TempDir,
|
||||
use crate::{
|
||||
smtp::{
|
||||
build_smtp,
|
||||
session::{TestSession, VerifyResponse},
|
||||
TempDir,
|
||||
},
|
||||
AssertConfig,
|
||||
};
|
||||
use smtp::core::{Inner, Session, State};
|
||||
|
||||
|
@ -22,6 +25,7 @@ data = "sqlite"
|
|||
lookup = "sqlite"
|
||||
blob = "sqlite"
|
||||
fts = "sqlite"
|
||||
directory = "local"
|
||||
|
||||
[store."sqlite"]
|
||||
type = "sqlite"
|
||||
|
@ -74,6 +78,7 @@ async fn auth() {
|
|||
let mut config = Config::new(tmp_dir.update_config(CONFIG)).unwrap();
|
||||
let stores = Stores::parse_all(&mut config).await;
|
||||
let core = Core::parse(&mut config, stores, Default::default()).await;
|
||||
config.assert_no_errors();
|
||||
|
||||
// EHLO should not advertise plain text auth without TLS
|
||||
let mut session = Session::test(build_smtp(core, Inner::default()));
|
||||
|
|
|
@ -11,10 +11,13 @@ use utils::config::Config;
|
|||
|
||||
use smtp::core::{Inner, Session};
|
||||
|
||||
use crate::smtp::{
|
||||
build_smtp,
|
||||
session::{TestSession, VerifyResponse},
|
||||
TempDir,
|
||||
use crate::{
|
||||
smtp::{
|
||||
build_smtp,
|
||||
session::{TestSession, VerifyResponse},
|
||||
TempDir,
|
||||
},
|
||||
AssertConfig,
|
||||
};
|
||||
|
||||
const CONFIG: &str = r#"
|
||||
|
@ -23,6 +26,7 @@ data = "sqlite"
|
|||
lookup = "sqlite"
|
||||
blob = "sqlite"
|
||||
fts = "sqlite"
|
||||
directory = "local"
|
||||
|
||||
[store."sqlite"]
|
||||
type = "sqlite"
|
||||
|
@ -72,6 +76,7 @@ async fn vrfy_expn() {
|
|||
let mut config = Config::new(tmp_dir.update_config(CONFIG)).unwrap();
|
||||
let stores = Stores::parse_all(&mut config).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
|
||||
let mut session = Session::test(build_smtp(core, Inner::default()));
|
||||
|
|
Loading…
Reference in a new issue