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> {
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

View file

@ -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);
}
}

View file

@ -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;

View file

@ -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);
}

View file

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

View file

@ -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);
}
}
}
}

View file

@ -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",
}
}
}

View file

@ -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)
}
}

View file

@ -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)

View file

@ -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();

View file

@ -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,

View file

@ -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.",
)));

View file

@ -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

View file

@ -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()
}
);

View file

@ -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),
}
}

View file

@ -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()
}
);

View file

@ -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;

View file

@ -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()));

View file

@ -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()));