Test fixes - part 3

This commit is contained in:
mdecimus 2024-09-18 11:18:43 +02:00
parent 6b7dac0fcb
commit d0303aefa8
21 changed files with 674 additions and 387 deletions

View file

@ -7,8 +7,9 @@
use mail_send::Credentials;
use store::{
write::{DirectoryClass, ValueClass},
IterateParams, Store, ValueKey,
Deserialize, IterateParams, Store, ValueKey,
};
use trc::AddContext;
use crate::{Principal, QueryBy, Type};
@ -124,12 +125,16 @@ impl DirectoryStore for Store {
ValueKey::from(ValueClass::Directory(DirectoryClass::EmailToId(
vec![u8::MAX; 10],
))),
)
.no_values(),
|key, _| {
),
|key, value| {
let key =
std::str::from_utf8(key.get(1..).unwrap_or_default()).unwrap_or_default();
if key.split('@').next().unwrap_or(key).contains(address) {
if key.split('@').next().unwrap_or(key).contains(address)
&& PrincipalInfo::deserialize(value)
.caused_by(trc::location!())?
.typ
!= Type::List
{
results.push(key.to_string());
}
Ok(true)
@ -143,15 +148,23 @@ impl DirectoryStore for Store {
async fn expn(&self, address: &str) -> trc::Result<Vec<String>> {
let mut results = Vec::new();
for account_id in self.email_to_ids(address).await? {
if let Some(email) = self
.get_value::<Principal>(ValueKey::from(ValueClass::Directory(
DirectoryClass::Principal(account_id),
)))
.await?
.and_then(|mut p| p.take_str(PrincipalField::Emails))
{
results.push(email);
if let Some(ptype) = self
.get_value::<PrincipalInfo>(ValueKey::from(ValueClass::Directory(
DirectoryClass::EmailToId(address.as_bytes().to_vec()),
)))
.await?
.filter(|p| p.typ == Type::List)
{
for account_id in self.get_members(ptype.id).await? {
if let Some(email) = self
.get_value::<Principal>(ValueKey::from(ValueClass::Directory(
DirectoryClass::Principal(account_id),
)))
.await?
.and_then(|mut p| p.take_str(PrincipalField::Emails))
{
results.push(email);
}
}
}

View file

@ -942,23 +942,12 @@ impl ManageDirectory for Store {
.or_else(|| change.field.map_internal_roles(&member))
.ok_or_else(|| not_found(member.clone()))?;
let expected_type = match change.field {
PrincipalField::MemberOf => Type::Group,
PrincipalField::Lists => Type::List,
PrincipalField::Roles => Type::Role,
_ => unreachable!(),
};
if member_info.typ != expected_type {
return Err(error(
format!("Invalid {} value", change.field.as_str()),
format!(
"Principal {member:?} is not a {}.",
expected_type.as_str()
)
.into(),
));
}
validate_member_of(
change.field,
principal.inner.typ,
member_info.typ,
&member,
)?;
if !member_of.contains(&member_info.id) {
batch.set(
@ -1009,23 +998,12 @@ impl ManageDirectory for Store {
.ok_or_else(|| not_found(member.clone()))?;
if !member_of.contains(&member_info.id) {
let expected_type = match change.field {
PrincipalField::MemberOf => Type::Group,
PrincipalField::Lists => Type::List,
PrincipalField::Roles => Type::Role,
_ => unreachable!(),
};
if member_info.typ != expected_type {
return Err(error(
format!("Invalid {} value", change.field.as_str()),
format!(
"Principal {member:?} is not a {}.",
expected_type.as_str()
)
.into(),
));
}
validate_member_of(
change.field,
principal.inner.typ,
member_info.typ,
&member,
)?;
batch.set(
ValueClass::Directory(DirectoryClass::MemberOf {
@ -1664,6 +1642,38 @@ impl From<Principal> for MaybeDynamicValue {
}
}
fn validate_member_of(
field: PrincipalField,
typ: Type,
member_type: Type,
member_name: &str,
) -> trc::Result<()> {
let expected_types = match (field, typ) {
(PrincipalField::MemberOf, Type::Individual) => &[Type::Group, Type::Individual][..],
(PrincipalField::MemberOf, Type::Group) => &[Type::Group][..],
(PrincipalField::Lists, Type::Individual | Type::Group) => &[Type::List][..],
(PrincipalField::Roles, Type::Individual | Type::Tenant | Type::Role) => &[Type::Role][..],
_ => &[][..],
};
if expected_types.is_empty() || !expected_types.contains(&member_type) {
Err(error(
format!("Invalid {} value", field.as_str()),
format!(
"Principal {member_name:?} is not a {}.",
expected_types
.iter()
.map(|t| t.as_str().to_string())
.collect::<Vec<_>>()
.join(", ")
)
.into(),
))
} else {
Ok(())
}
}
#[derive(Clone, Copy)]
struct DynamicPrincipalInfo {
typ: Type,

View file

@ -474,6 +474,24 @@ impl Store {
.caused_by(trc::location!())?;
}
// Delete property counters (TODO: make this more elegant)
self.delete_range(
ValueKey {
account_id,
collection: 1,
document_id: 0,
class: ValueClass::Property(84),
},
ValueKey {
account_id,
collection: 1,
document_id: u32::MAX,
class: ValueClass::Property(84),
},
)
.await
.caused_by(trc::location!())?;
Ok(())
}

View file

@ -18,7 +18,7 @@ use mail_send::Credentials;
use store::{
roaring::RoaringBitmap,
write::{BatchBuilder, BitmapClass, ValueClass},
BitmapKey, ValueKey,
BitmapKey, Store, ValueKey,
};
use crate::directory::{DirectoryTest, IntoTestPrincipal, TestPrincipal};
@ -718,3 +718,197 @@ async fn internal_directory() {
);
}
}
#[allow(async_fn_in_trait)]
pub trait TestInternalDirectory {
async fn create_test_user(&self, login: &str, secret: &str, name: &str, emails: &[&str])
-> u32;
async fn create_test_group(&self, login: &str, name: &str, emails: &[&str]) -> u32;
async fn create_test_list(&self, login: &str, name: &str, emails: &[&str]) -> u32;
async fn set_test_quota(&self, login: &str, quota: u32);
async fn add_to_group(&self, login: &str, group: &str);
async fn remove_from_group(&self, login: &str, group: &str);
async fn remove_test_alias(&self, login: &str, alias: &str);
async fn create_test_domains(&self, domains: &[&str]);
}
impl TestInternalDirectory for Store {
async fn create_test_user(
&self,
login: &str,
secret: &str,
name: &str,
emails: &[&str],
) -> u32 {
let role = if login == "admin" { "admin" } else { "user" };
self.create_test_domains(emails).await;
if let Some(principal) = self.query(QueryBy::Name(login), false).await.unwrap() {
self.update_principal(
QueryBy::Id(principal.id()),
vec![
PrincipalUpdate::set(
PrincipalField::Secrets,
PrincipalValue::StringList(vec![secret.to_string()]),
),
PrincipalUpdate::set(
PrincipalField::Description,
PrincipalValue::String(name.to_string()),
),
PrincipalUpdate::set(
PrincipalField::Emails,
PrincipalValue::StringList(emails.iter().map(|s| s.to_string()).collect()),
),
PrincipalUpdate::add_item(
PrincipalField::Roles,
PrincipalValue::String(role.to_string()),
),
],
None,
)
.await
.unwrap();
principal.id()
} else {
self.create_principal(
Principal::new(0, Type::Individual)
.with_field(PrincipalField::Name, login.to_string())
.with_field(PrincipalField::Description, name.to_string())
.with_field(
PrincipalField::Secrets,
PrincipalValue::StringList(vec![secret.to_string()]),
)
.with_field(
PrincipalField::Emails,
PrincipalValue::StringList(emails.iter().map(|s| s.to_string()).collect()),
)
.with_field(
PrincipalField::Roles,
PrincipalValue::StringList(vec![role.to_string()]),
),
None,
)
.await
.unwrap()
}
}
async fn create_test_group(&self, login: &str, name: &str, emails: &[&str]) -> u32 {
self.create_test_domains(emails).await;
if let Some(principal) = self.query(QueryBy::Name(login), false).await.unwrap() {
principal.id()
} else {
self.create_principal(
Principal::new(0, Type::Group)
.with_field(PrincipalField::Name, login.to_string())
.with_field(PrincipalField::Description, name.to_string())
.with_field(
PrincipalField::Emails,
PrincipalValue::StringList(emails.iter().map(|s| s.to_string()).collect()),
)
.with_field(
PrincipalField::Roles,
PrincipalValue::StringList(vec!["user".to_string()]),
),
None,
)
.await
.unwrap()
}
}
async fn create_test_list(&self, login: &str, name: &str, members: &[&str]) -> u32 {
if let Some(principal) = self.query(QueryBy::Name(login), false).await.unwrap() {
principal.id()
} else {
self.create_test_domains(&[login]).await;
self.create_principal(
Principal::new(0, Type::List)
.with_field(PrincipalField::Name, login.to_string())
.with_field(PrincipalField::Description, name.to_string())
.with_field(
PrincipalField::Members,
PrincipalValue::StringList(members.iter().map(|s| s.to_string()).collect()),
)
.with_field(
PrincipalField::Emails,
PrincipalValue::StringList(vec![login.to_string()]),
),
None,
)
.await
.unwrap()
}
}
async fn set_test_quota(&self, login: &str, quota: u32) {
self.update_principal(
QueryBy::Name(login),
vec![PrincipalUpdate::set(
PrincipalField::Quota,
PrincipalValue::Integer(quota as u64),
)],
None,
)
.await
.unwrap();
}
async fn add_to_group(&self, login: &str, group: &str) {
self.update_principal(
QueryBy::Name(login),
vec![PrincipalUpdate::add_item(
PrincipalField::MemberOf,
PrincipalValue::String(group.to_string()),
)],
None,
)
.await
.unwrap();
}
async fn remove_from_group(&self, login: &str, group: &str) {
self.update_principal(
QueryBy::Name(login),
vec![PrincipalUpdate::remove_item(
PrincipalField::MemberOf,
PrincipalValue::String(group.to_string()),
)],
None,
)
.await
.unwrap();
}
async fn remove_test_alias(&self, login: &str, alias: &str) {
self.update_principal(
QueryBy::Name(login),
vec![PrincipalUpdate::remove_item(
PrincipalField::Emails,
PrincipalValue::String(alias.to_string()),
)],
None,
)
.await
.unwrap();
}
async fn create_test_domains(&self, domains: &[&str]) {
for domain in domains {
let domain = domain.rsplit_once('@').map_or(*domain, |(_, d)| d);
if self
.query(QueryBy::Name(domain), false)
.await
.unwrap()
.is_none()
{
self.create_principal(
Principal::new(0, Type::Domain)
.with_field(PrincipalField::Name, domain.to_string()),
None,
)
.await
.unwrap();
}
}
}
}

View file

@ -36,7 +36,6 @@ use common::{
use ::store::Stores;
use ahash::AHashSet;
use directory::backend::internal::manage::ManageDirectory;
use imap::core::{ImapSessionManager, Inner, IMAP};
use imap_proto::ResponseType;
use jmap::{api::JmapSessionManager, JMAP};
@ -49,7 +48,9 @@ use tokio::{
};
use utils::config::Config;
use crate::{add_test_certs, directory::DirectoryStore, store::TempDir, AssertConfig};
use crate::{
add_test_certs, directory::internal::TestInternalDirectory, store::TempDir, AssertConfig,
};
const SERVER: &str = r#"
[lookup.default]
@ -98,7 +99,7 @@ reject-non-fqdn = false
[session.rcpt]
relay = [ { if = "!is_empty(authenticated_as)", then = true },
{ else = false } ]
directory = "'auth'"
directory = "'{STORE}'"
[session.rcpt.errors]
total = 5
@ -187,7 +188,7 @@ data = "{STORE}"
fts = "{STORE}"
blob = "{STORE}"
lookup = "{STORE}"
directory = "auth"
directory = "{STORE}"
[jmap.protocol]
set.max-objects = 100000
@ -252,17 +253,9 @@ verify = "SELECT address FROM emails WHERE address LIKE '%' || ? || '%' AND type
expand = "SELECT p.address FROM emails AS p JOIN emails AS l ON p.name = l.name WHERE p.type = 'primary' AND l.address = ? AND l.type = 'list' ORDER BY p.address LIMIT 50"
domains = "SELECT 1 FROM emails WHERE address LIKE '%@' || ? LIMIT 1"
[directory."auth"]
type = "sql"
store = "auth"
[directory."auth".columns]
name = "name"
description = "description"
secret = "secret"
email = "address"
quota = "quota"
class = "type"
[directory."{STORE}"]
type = "internal"
store = "{STORE}"
[oauth]
key = "parerga_und_paralipomena"
@ -386,48 +379,56 @@ async fn init_imap_tests(store_id: &str, delete_if_exists: bool) -> IMAPTest {
};
});
// Create tables and test accounts
let lookup = DirectoryStore {
store: shared_core
.load()
.storage
.lookups
.get("auth")
.unwrap()
.clone(),
};
lookup.create_test_directory().await;
lookup
.create_test_user("admin", "secret", "Superuser")
.await;
lookup
.create_test_user_with_email("jdoe@example.com", "secret", "John Doe")
.await;
lookup
.create_test_user_with_email("jane.smith@example.com", "secret", "Jane Smith")
.await;
lookup
.create_test_user_with_email("foobar@example.com", "secret", "Bill Foobar")
.await;
lookup
.create_test_user_with_email("popper@example.com", "secret", "Karl Popper")
.await;
lookup
.create_test_group_with_email("support@example.com", "Support Group")
.await;
lookup
.add_to_group("jane.smith@example.com", "support@example.com")
.await;
if delete_if_exists {
store.destroy().await;
}
// Assign Id 0 to admin (required for some tests)
// Create tables and test accounts
store
.get_or_create_principal_id("admin", directory::Type::Individual)
.await
.unwrap();
.create_test_user("admin", "secret", "Superuser", &[])
.await;
store
.create_test_user(
"jdoe@example.com",
"secret",
"John Doe",
&["jdoe@example.com"],
)
.await;
store
.create_test_user(
"jane.smith@example.com",
"secret",
"Jane Smith",
&["jane.smith@example.com"],
)
.await;
store
.create_test_user(
"foobar@example.com",
"secret",
"Bill Foobar",
&["foobar@example.com"],
)
.await;
store
.create_test_user(
"popper@example.com",
"secret",
"Karl Popper",
&["popper@example.com"],
)
.await;
store
.create_test_group(
"support@example.com",
"Support Group",
&["support@example.com"],
)
.await;
store
.add_to_group("jane.smith@example.com", "support@example.com")
.await;
IMAPTest {
jmap: JMAP::from(jmap.clone()).into(),

View file

@ -4,7 +4,6 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use directory::backend::internal::manage::ManageDirectory;
use jmap::mailbox::{INBOX_ID, TRASH_ID};
use jmap_client::{
core::{
@ -19,7 +18,10 @@ use jmap_proto::types::id::Id;
use std::fmt::Debug;
use store::ahash::AHashMap;
use crate::jmap::{assert_is_empty, mailbox::destroy_all_mailboxes, test_account_login};
use crate::{
directory::internal::TestInternalDirectory,
jmap::{assert_is_empty, mailbox::destroy_all_mailboxes, test_account_login},
};
use super::JMAPTest;
@ -31,53 +33,48 @@ pub async fn test(params: &mut JMAPTest) {
let inbox_id = Id::new(INBOX_ID as u64).to_string();
let trash_id = Id::new(TRASH_ID as u64).to_string();
params
.directory
.create_test_user_with_email("jdoe@example.com", "12345", "John Doe")
.await;
params
.directory
.create_test_user_with_email("jane.smith@example.com", "abcde", "Jane Smith")
.await;
params
.directory
.create_test_user_with_email("bill@example.com", "098765", "Bill Foobar")
.await;
params
.directory
.create_test_group_with_email("sales@example.com", "Sales Group")
.await;
let john_id: Id = server
.core
.storage
.data
.get_or_create_principal_id("jdoe@example.com", directory::Type::Individual)
.create_test_user(
"jdoe@example.com",
"12345",
"John Doe",
&["jdoe@example.com"],
)
.await
.unwrap()
.into();
let jane_id: Id = server
.core
.storage
.data
.get_or_create_principal_id("jane.smith@example.com", directory::Type::Individual)
.create_test_user(
"jane.smith@example.com",
"abcde",
"Jane Smith",
&["jane.smith@example.com"],
)
.await
.unwrap()
.into();
let bill_id: Id = server
.core
.storage
.data
.get_or_create_principal_id("bill@example.com", directory::Type::Individual)
.create_test_user(
"bill@example.com",
"098765",
"Bill Foobar",
&["bill@example.com"],
)
.await
.unwrap()
.into();
let sales_id: Id = server
.core
.storage
.data
.get_or_create_principal_id("sales@example.com", directory::Type::Individual)
.create_test_group("sales@example.com", "Sales Group", &["sales@example.com"])
.await
.unwrap()
.into();
// Authenticate all accounts
@ -666,8 +663,10 @@ pub async fn test(params: &mut JMAPTest) {
// Add John and Jane to the Sales group
for name in ["jdoe@example.com", "jane.smith@example.com"] {
params
.directory
server
.core
.storage
.data
.add_to_group(name, "sales@example.com")
.await;
}
@ -765,8 +764,10 @@ pub async fn test(params: &mut JMAPTest) {
);
// Remove John from the sales group
params
.directory
server
.core
.storage
.data
.remove_from_group("jdoe@example.com", "sales@example.com")
.await;
server.inner.sessions.clear();

View file

@ -11,7 +11,6 @@ use std::{
};
use common::listener::blocked::BLOCKED_IP_KEY;
use directory::backend::internal::manage::ManageDirectory;
use imap_proto::ResponseType;
use jmap_client::{
client::{Client, Credentials},
@ -22,6 +21,7 @@ use jmap_proto::types::id::Id;
use store::write::now;
use crate::{
directory::internal::TestInternalDirectory,
imap::{ImapConnection, Type},
jmap::{assert_is_empty, mailbox::destroy_all_mailboxes},
};
@ -33,24 +33,20 @@ pub async fn test(params: &mut JMAPTest) {
// Create test account
let server = params.server.clone();
params
.directory
.create_test_user_with_email("jdoe@example.com", "12345", "John Doe")
.await;
let account_id = Id::from(
server
.core
.storage
.data
.get_or_create_principal_id("jdoe@example.com", directory::Type::Individual)
.await
.unwrap(),
.create_test_user(
"jdoe@example.com",
"12345",
"John Doe",
&["jdoe@example.com", "john.doe@example.com"],
)
.await,
)
.to_string();
params
.directory
.link_test_address("jdoe@example.com", "john.doe@example.com", "alias")
.await;
// Reset rate limiters
server.inner.concurrency_limiter.clear();

View file

@ -7,7 +7,6 @@
use std::time::{Duration, Instant};
use bytes::Bytes;
use directory::backend::internal::manage::ManageDirectory;
use jmap::auth::oauth::{
DeviceAuthResponse, ErrorType, OAuthCodeRequest, OAuthMetadata, TokenResponse,
};
@ -19,7 +18,10 @@ use jmap_proto::types::id::Id;
use serde::de::DeserializeOwned;
use store::ahash::AHashMap;
use crate::jmap::{assert_is_empty, mailbox::destroy_all_mailboxes, ManagementApi};
use crate::{
directory::internal::TestInternalDirectory,
jmap::{assert_is_empty, mailbox::destroy_all_mailboxes, ManagementApi},
};
use super::JMAPTest;
@ -36,18 +38,18 @@ pub async fn test(params: &mut JMAPTest) {
// Create test account
let server = params.server.clone();
params
.directory
.create_test_user_with_email("jdoe@example.com", "12345", "John Doe")
.await;
let john_id = Id::from(
server
.core
.storage
.data
.get_or_create_principal_id("jdoe@example.com", directory::Type::Individual)
.await
.unwrap(),
.create_test_user(
"jdoe@example.com",
"12345",
"John Doe",
&["jdoe@example.com"],
)
.await,
)
.to_string();

View file

@ -4,32 +4,33 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use directory::backend::internal::manage::ManageDirectory;
use jmap::mailbox::INBOX_ID;
use jmap_proto::types::id::Id;
use serde_json::Value;
use crate::jmap::{assert_is_empty, jmap_json_request, mailbox::destroy_all_mailboxes};
use crate::{
directory::internal::TestInternalDirectory,
jmap::{assert_is_empty, jmap_json_request, mailbox::destroy_all_mailboxes},
};
use super::JMAPTest;
pub async fn test(params: &mut JMAPTest) {
println!("Running blob tests...");
let server = params.server.clone();
params
.directory
.create_test_user_with_email("jdoe@example.com", "12345", "John Doe")
.await;
let account_id = Id::from(
server
.core
.storage
.data
.get_or_create_principal_id("jdoe@example.com", directory::Type::Individual)
.await
.unwrap(),
.create_test_user(
"jdoe@example.com",
"12345",
"John Doe",
&["jdoe@example.com"],
)
.await,
);
server.core.storage.data.blob_expire_all().await;
// Blob/set simple test

View file

@ -6,14 +6,16 @@
use std::path::PathBuf;
use directory::backend::internal::manage::ManageDirectory;
use jmap::email::crypto::{
try_parse_certs, Algorithm, EncryptMessage, EncryptionMethod, EncryptionParams, EncryptionType,
};
use jmap_proto::types::id::Id;
use mail_parser::{MessageParser, MimeHeaders};
use crate::jmap::{delivery::SmtpConnection, ManagementApi};
use crate::{
directory::internal::TestInternalDirectory,
jmap::{delivery::SmtpConnection, ManagementApi},
};
use super::JMAPTest;
@ -23,18 +25,18 @@ pub async fn test(params: &mut JMAPTest) {
// Create test account
let server = params.server.clone();
let client = &mut params.client;
params
.directory
.create_test_user_with_email("jdoe@example.com", "12345", "John Doe")
.await;
let account_id = Id::from(
server
.core
.storage
.data
.get_or_create_principal_id("jdoe@example.com", directory::Type::Individual)
.await
.unwrap(),
.create_test_user(
"jdoe@example.com",
"12345",
"John Doe",
&["jdoe@example.com"],
)
.await,
)
.to_string();

View file

@ -6,7 +6,6 @@
use std::time::Duration;
use directory::backend::internal::manage::ManageDirectory;
use jmap::mailbox::{INBOX_ID, JUNK_ID};
use jmap_proto::types::{collection::Collection, id::Id, property::Property};
@ -15,7 +14,10 @@ use tokio::{
net::TcpStream,
};
use crate::jmap::{assert_is_empty, mailbox::destroy_all_mailboxes};
use crate::{
directory::internal::TestInternalDirectory,
jmap::{assert_is_empty, mailbox::destroy_all_mailboxes},
};
use super::JMAPTest;
@ -24,65 +26,54 @@ pub async fn test(params: &mut JMAPTest) {
// Create a domain name and a test account
let server = params.server.clone();
params
.directory
.create_test_user_with_email("jdoe@example.com", "12345", "John Doe")
.await;
params
.directory
.create_test_user_with_email("jane@example.com", "abcdef", "Jane Smith")
.await;
params
.directory
.create_test_user_with_email("bill@example.com", "098765", "Bill Foobar")
.await;
let account_id_1 = Id::from(
server
.core
.storage
.data
.get_or_create_principal_id("jdoe@example.com", directory::Type::Individual)
.await
.unwrap(),
)
.to_string();
let account_id_2 = Id::from(
server
.core
.storage
.data
.get_or_create_principal_id("jane@example.com", directory::Type::Individual)
.await
.unwrap(),
)
.to_string();
let account_id_3 = Id::from(
server
.core
.storage
.data
.get_or_create_principal_id("bill@example.com", directory::Type::Individual)
.await
.unwrap(),
)
.to_string();
params
.directory
.link_test_address("jdoe@example.com", "john.doe@example.com", "alias")
.await;
let mut account_id_1 = String::new();
let mut account_id_2 = String::new();
let mut account_id_3 = String::new();
for (id, email, password, name, aliases) in [
(
&mut account_id_1,
"jdoe@example.com",
"12345",
"John Doe",
Some(&["jdoe@example.com", "john.doe@example.com"][..]),
),
(
&mut account_id_2,
"jane@example.com",
"abcdef",
"Jane Smith",
None,
),
(
&mut account_id_3,
"bill@example.com",
"098765",
"Bill Foobar",
None,
),
] {
*id = Id::from(
server
.core
.storage
.data
.create_test_user(email, password, name, aliases.unwrap_or(&[email][..]))
.await,
)
.to_string();
}
// Create a mailing list
params
.directory
.link_test_address("jdoe@example.com", "members@example.com", "list")
.await;
params
.directory
.link_test_address("jane@example.com", "members@example.com", "list")
.await;
params
.directory
.link_test_address("bill@example.com", "members@example.com", "list")
server
.core
.storage
.data
.create_test_list(
"members@example.com",
"Mailing List",
&["jdoe@example.com", "jane@example.com", "bill@example.com"],
)
.await;
// Delivering to individuals
@ -225,8 +216,11 @@ pub async fn test(params: &mut JMAPTest) {
// Removing members from the mailing list and chunked ingest
params
.directory
.remove_test_alias("jdoe@example.com", "members@example.com")
.server
.core
.storage
.data
.remove_from_group("jdoe@example.com", "members@example.com")
.await;
lmtp.ingest_chunked(
"bill@example.com",

View file

@ -5,7 +5,6 @@
*/
use ahash::AHashMap;
use directory::backend::internal::manage::ManageDirectory;
use jmap_client::{
core::set::{SetError, SetErrorType, SetObject},
email_submission::{query::Filter, Address, Delivered, DeliveryStatus, Displayed, UndoStatus},
@ -26,8 +25,9 @@ use tokio::{
sync::mpsc,
};
use crate::jmap::{
assert_is_empty, email_set::assert_email_properties, mailbox::destroy_all_mailboxes,
use crate::{
directory::internal::TestInternalDirectory,
jmap::{assert_is_empty, email_set::assert_email_properties, mailbox::destroy_all_mailboxes},
};
use super::JMAPTest;
@ -76,22 +76,18 @@ pub async fn test(params: &mut JMAPTest) {
// Create a test account
let server = params.server.clone();
params
.directory
.create_test_user_with_email("jdoe@example.com", "12345", "John Doe")
.await;
params
.directory
.link_test_address("jdoe@example.com", "john.doe@example.com", "alias")
.await;
let account_id = Id::from(
server
.core
.storage
.data
.get_or_create_principal_id("jdoe@example.com", directory::Type::Individual)
.await
.unwrap(),
.create_test_user(
"jdoe@example.com",
"12345",
"John Doe",
&["jdoe@example.com", "john.doe@example.com"],
)
.await,
)
.to_string();

View file

@ -35,6 +35,7 @@ use trc::{
use utils::config::{cron::SimpleCron, Config};
use crate::{
directory::internal::TestInternalDirectory,
imap::{ImapConnection, Type},
jmap::delivery::SmtpConnection,
AssertConfig,
@ -112,8 +113,17 @@ pub async fn test(params: &mut JMAPTest) {
// Create test account
params
.directory
.create_test_user_with_email("jdoe@example.com", "secret", "John Doe")
.server
.shared_core
.load()
.storage
.data
.create_test_user(
"jdoe@example.com",
"secret",
"John Doe",
&["jdoe@example.com"],
)
.await;
alerts(&params.server.shared_core.load()).await;
@ -121,10 +131,39 @@ pub async fn test(params: &mut JMAPTest) {
tracing(params).await;
metrics(params).await;
// Disable Enterprise
let mut core = params.server.shared_core.load_full().as_ref().clone();
core.enterprise = None;
params.server.shared_core.store(core.into());
params.server.shared_core.store(
params
.server
.shared_core
.load_full()
.as_ref()
.clone()
.enable_enterprise()
.into(),
);
}
pub trait EnterpriseCore {
fn enable_enterprise(self) -> Self;
}
impl EnterpriseCore for Core {
fn enable_enterprise(mut self) -> Self {
self.enterprise = Enterprise {
license: LicenseKey {
valid_to: now() + 3600,
valid_from: now() - 3600,
hostname: String::new(),
accounts: 100,
},
undelete: None,
trace_store: None,
metrics_store: None,
metrics_alerts: vec![],
}
.into();
self
}
}
async fn alerts(core: &Core) {

View file

@ -6,10 +6,13 @@
use std::time::Duration;
use crate::jmap::{
assert_is_empty, delivery::SmtpConnection, mailbox::destroy_all_mailboxes, test_account_login,
use crate::{
directory::internal::TestInternalDirectory,
jmap::{
assert_is_empty, delivery::SmtpConnection, mailbox::destroy_all_mailboxes,
test_account_login,
},
};
use directory::backend::internal::manage::ManageDirectory;
use futures::StreamExt;
use jmap::mailbox::INBOX_ID;
use jmap_client::{event_source::Changes, mailbox::Role, TypeState};
@ -25,20 +28,21 @@ pub async fn test(params: &mut JMAPTest) {
// Create test account
let server = params.server.clone();
params
.directory
.create_test_user_with_email("jdoe@example.com", "12345", "John Doe")
.await;
let account_id = Id::from(
server
.core
.storage
.data
.get_or_create_principal_id("jdoe@example.com", directory::Type::Individual)
.await
.unwrap(),
.create_test_user(
"jdoe@example.com",
"12345",
"John Doe",
&["jdoe@example.com"],
)
.await,
)
.to_string();
let client = test_account_login("jdoe@example.com", "12345").await;
let mut changes = client

View file

@ -23,7 +23,7 @@ use common::{
manager::config::{ConfigManager, Patterns},
Core, Ipc, IPC_CHANNEL_BUFFER,
};
use enterprise::insert_test_metrics;
use enterprise::{insert_test_metrics, EnterpriseCore};
use hyper::{header::AUTHORIZATION, Method};
use imap::core::{ImapSessionManager, IMAP};
use jmap::{api::JmapSessionManager, JMAP};
@ -44,7 +44,9 @@ use tokio::sync::{mpsc, watch};
use utils::{config::Config, map::ttl_dashmap::TtlMap, BlobHash};
use webhooks::{spawn_mock_webhook_endpoint, MockWebhookEndpoint};
use crate::{add_test_certs, directory::DirectoryStore, store::TempDir, AssertConfig};
use crate::{
add_test_certs, directory::internal::TestInternalDirectory, store::TempDir, AssertConfig,
};
pub mod auth_acl;
pub mod auth_limits;
@ -117,7 +119,7 @@ reject-non-fqdn = false
[session.rcpt]
relay = [ { if = "!is_empty(authenticated_as)", then = true },
{ else = false } ]
directory = "'auth'"
directory = "'{STORE}'"
[session.rcpt.errors]
total = 5
@ -196,7 +198,7 @@ data = "{STORE}"
fts = "{STORE}"
blob = "{STORE}"
lookup = "{STORE}"
directory = "auth"
directory = "{STORE}"
[spam.header]
is-spam = "X-Spam-Status: Yes"
@ -252,17 +254,9 @@ verify = "SELECT address FROM emails WHERE address LIKE '%' || ? || '%' AND type
expand = "SELECT p.address FROM emails AS p JOIN emails AS l ON p.name = l.name WHERE p.type = 'primary' AND l.address = ? AND l.type = 'list' ORDER BY p.address LIMIT 50"
domains = "SELECT 1 FROM emails WHERE address LIKE '%@' || ? LIMIT 1"
[directory."auth"]
type = "sql"
store = "auth"
[directory."auth".columns]
name = "name"
description = "description"
secret = "secret"
email = "address"
quota = "quota"
class = "type"
[directory."{STORE}"]
type = "internal"
store = "{STORE}"
[imap.auth]
allow-plain-text = true
@ -316,7 +310,7 @@ pub async fn jmap_tests() {
.await;
webhooks::test(&mut params).await;
/*email_query::test(&mut params, delete).await;
/* //email_query::test(&mut params, delete).await;
email_get::test(&mut params).await;
email_set::test(&mut params).await;
email_parse::test(&mut params).await;
@ -324,12 +318,12 @@ pub async fn jmap_tests() {
email_changes::test(&mut params).await;
email_query_changes::test(&mut params).await;
email_copy::test(&mut params).await;
thread_get::test(&mut params).await;
thread_merge::test(&mut params).await;
//thread_get::test(&mut params).await;
//thread_merge::test(&mut params).await;
mailbox::test(&mut params).await;
delivery::test(&mut params).await;
auth_acl::test(&mut params).await;
auth_limits::test(&mut params).await;*/
auth_limits::test(&mut params).await;
auth_oauth::test(&mut params).await;
event_source::test(&mut params).await;
push_subscription::test(&mut params).await;
@ -339,7 +333,7 @@ pub async fn jmap_tests() {
websocket::test(&mut params).await;
quota::test(&mut params).await;
crypto::test(&mut params).await;
blob::test(&mut params).await;
blob::test(&mut params).await;*/
purge::test(&mut params).await;
enterprise::test(&mut params).await;
@ -378,7 +372,6 @@ pub async fn jmap_metric_tests() {
pub struct JMAPTest {
server: Arc<JMAP>,
client: Client,
directory: DirectoryStore,
temp_dir: TempDir,
webhook: Arc<MockWebhookEndpoint>,
shutdown_tx: watch::Sender<bool>,
@ -531,7 +524,9 @@ async fn init_jmap_tests(store_id: &str, delete_if_exists: bool) -> JMAPTest {
.unwrap_or_default(),
};
let tracers = Telemetry::parse(&mut config, &stores);
let core = Core::parse(&mut config, stores, config_manager).await;
let core = Core::parse(&mut config, stores, config_manager)
.await
.enable_enterprise();
let store = core.storage.data.clone();
let shared_core = core.into_shared();
@ -599,25 +594,18 @@ async fn init_jmap_tests(store_id: &str, delete_if_exists: bool) -> JMAPTest {
};
});
// Create tables
let directory = DirectoryStore {
store: shared_core
.load()
.storage
.lookups
.get("auth")
.unwrap()
.clone(),
};
directory.create_test_directory().await;
directory
.create_test_user("admin", "secret", "Superuser")
.await;
if delete_if_exists {
store.destroy().await;
}
// Create tables
shared_core
.load()
.storage
.data
.create_test_user("admin", "secret", "Superuser", &[])
.await;
// Create client
let mut client = Client::new()
.credentials(Credentials::basic("admin", "secret"))
@ -632,7 +620,6 @@ async fn init_jmap_tests(store_id: &str, delete_if_exists: bool) -> JMAPTest {
server: JMAP::from(jmap).into(),
temp_dir,
client,
directory,
shutdown_tx,
webhook: spawn_mock_webhook_endpoint(),
}

View file

@ -5,7 +5,7 @@
*/
use ahash::AHashSet;
use directory::backend::internal::manage::ManageDirectory;
use directory::{backend::internal::manage::ManageDirectory, QueryBy};
use imap_proto::ResponseType;
use jmap::{
mailbox::{INBOX_ID, JUNK_ID, TRASH_ID},
@ -17,7 +17,11 @@ use store::{
IterateParams, LogKey, U32_LEN, U64_LEN,
};
use crate::imap::{AssertResult, ImapConnection, Type};
use crate::{
directory::internal::TestInternalDirectory,
imap::{AssertResult, ImapConnection, Type},
jmap::assert_is_empty,
};
use super::JMAPTest;
@ -30,17 +34,18 @@ pub async fn test(params: &mut JMAPTest) {
let junk_id = Id::from(JUNK_ID).to_string();
// Connect to IMAP
params
.directory
.create_test_user_with_email("jdoe@example.com", "12345", "John Doe")
.await;
let account_id = server
.core
.storage
.data
.get_or_create_principal_id("jdoe@example.com", directory::Type::Individual)
.await
.unwrap();
.create_test_user(
"jdoe@example.com",
"12345",
"John Doe",
&["jdoe@example.com"],
)
.await;
let mut imap = ImapConnection::connect(b"_x ").await;
imap.assert_read(Type::Untagged, ResponseType::Ok).await;
imap.send("LOGIN \"jdoe@example.com\" \"12345\"").await;
@ -188,6 +193,16 @@ pub async fn test(params: &mut JMAPTest) {
change
);
}
// Delete account
server
.core
.storage
.data
.delete_principal(QueryBy::Id(account_id))
.await
.unwrap();
assert_is_empty(server).await;
}
async fn get_changes(server: &JMAP) -> AHashSet<(u64, u8)> {

View file

@ -14,7 +14,6 @@ use std::{
use base64::{engine::general_purpose, Engine};
use common::{config::server::Servers, listener::SessionData, Core};
use directory::backend::internal::manage::ManageDirectory;
use ece::EcKeyComponents;
use hyper::{body, header::CONTENT_ENCODING, server::conn::http1, service::service_fn, StatusCode};
use hyper_util::rt::TokioIo;
@ -34,6 +33,7 @@ use utils::config::Config;
use crate::{
add_test_certs,
directory::internal::TestInternalDirectory,
jmap::{assert_is_empty, mailbox::destroy_all_mailboxes, test_account_login},
AssertConfig,
};
@ -64,19 +64,20 @@ pub async fn test(params: &mut JMAPTest) {
// Create test account
let server = params.server.clone();
params
.directory
.create_test_user_with_email("jdoe@example.com", "12345", "John Doe")
.await;
let account_id = Id::from(
server
.core
.storage
.data
.get_or_create_principal_id("jdoe@example.com", directory::Type::Individual)
.await
.unwrap(),
.create_test_user(
"jdoe@example.com",
"12345",
"John Doe",
&["jdoe@example.com"],
)
.await,
);
params.client.set_default_account_id(account_id);
let client = test_account_login("jdoe@example.com", "12345").await;

View file

@ -4,11 +4,13 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use crate::jmap::{
assert_is_empty, delivery::SmtpConnection, emails_purge_tombstoned, jmap_raw_request,
mailbox::destroy_all_mailboxes, test_account_login,
use crate::{
directory::internal::TestInternalDirectory,
jmap::{
assert_is_empty, delivery::SmtpConnection, emails_purge_tombstoned, jmap_raw_request,
mailbox::destroy_all_mailboxes, test_account_login,
},
};
use directory::backend::internal::manage::ManageDirectory;
use jmap::{blob::upload::DISABLE_UPLOAD_QUOTA, mailbox::INBOX_ID};
use jmap_client::{
core::set::{SetErrorType, SetObject},
@ -21,38 +23,43 @@ use super::JMAPTest;
pub async fn test(params: &mut JMAPTest) {
println!("Running quota tests...");
let server = params.server.clone();
params
.directory
.create_test_user_with_email("jdoe@example.com", "12345", "John Doe")
.await;
params
.directory
.create_test_user_with_email("robert@example.com", "aabbcc", "Robert Foobar")
.await;
let other_account_id = Id::from(
server
.core
.storage
.data
.get_or_create_principal_id("jdoe@example.com", directory::Type::Individual)
.await
.unwrap(),
);
let account_id = Id::from(
server
.core
.storage
.data
.get_or_create_principal_id("robert@example.com", directory::Type::Individual)
.await
.unwrap(),
);
params
.directory
let mut account_id = Id::from(0u64);
let mut other_account_id = Id::from(0u64);
for (id, email, password, name) in [
(
&mut other_account_id,
"jdoe@example.com",
"12345",
"John Doe",
),
(
&mut account_id,
"robert@example.com",
"aabbcc",
"Robert Foobar",
),
] {
*id = Id::from(
server
.core
.storage
.data
.create_test_user(email, password, name, &[email][..])
.await,
);
}
server
.core
.storage
.data
.set_test_quota("robert@example.com", 1024)
.await;
params
.directory
server
.core
.storage
.data
.add_to_group("robert@example.com", "jdoe@example.com")
.await;

View file

@ -4,7 +4,6 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use directory::backend::internal::manage::ManageDirectory;
use jmap_client::{
core::set::{SetError, SetErrorType},
email, mailbox,
@ -18,11 +17,14 @@ use std::{
time::{Duration, Instant},
};
use crate::jmap::{
assert_is_empty,
delivery::SmtpConnection,
email_submission::{assert_message_delivery, spawn_mock_smtp_server, MockMessage},
mailbox::destroy_all_mailboxes,
use crate::{
directory::internal::TestInternalDirectory,
jmap::{
assert_is_empty,
delivery::SmtpConnection,
email_submission::{assert_message_delivery, spawn_mock_smtp_server, MockMessage},
mailbox::destroy_all_mailboxes,
},
};
use super::JMAPTest;
@ -33,18 +35,18 @@ pub async fn test(params: &mut JMAPTest) {
let client = &mut params.client;
// Create test account
params
.directory
.create_test_user_with_email("jdoe@example.com", "12345", "John Doe")
.await;
let account_id = Id::from(
server
.core
.storage
.data
.get_or_create_principal_id("jdoe@example.com", directory::Type::Individual)
.await
.unwrap(),
.create_test_user(
"jdoe@example.com",
"12345",
"John Doe",
&["jdoe@example.com"],
)
.await,
)
.to_string();
client.set_default_account_id(&account_id);

View file

@ -6,17 +6,19 @@
use chrono::{TimeDelta, Utc};
use directory::backend::internal::manage::ManageDirectory;
use jmap_proto::types::id::Id;
use std::time::Instant;
use crate::jmap::{
assert_is_empty,
delivery::SmtpConnection,
email_submission::{
assert_message_delivery, expect_nothing, spawn_mock_smtp_server, MockMessage,
use crate::{
directory::internal::TestInternalDirectory,
jmap::{
assert_is_empty,
delivery::SmtpConnection,
email_submission::{
assert_message_delivery, expect_nothing, spawn_mock_smtp_server, MockMessage,
},
mailbox::destroy_all_mailboxes,
},
mailbox::destroy_all_mailboxes,
};
use super::JMAPTest;
@ -27,18 +29,18 @@ pub async fn test(params: &mut JMAPTest) {
// Create test account
let server = params.server.clone();
let client = &mut params.client;
params
.directory
.create_test_user_with_email("jdoe@example.com", "12345", "John Doe")
.await;
let account_id = Id::from(
server
.core
.storage
.data
.get_or_create_principal_id("jdoe@example.com", directory::Type::Individual)
.await
.unwrap(),
.create_test_user(
"jdoe@example.com",
"12345",
"John Doe",
&["jdoe@example.com"],
)
.await,
)
.to_string();
client.set_default_account_id(&account_id);

View file

@ -5,7 +5,6 @@
*/
use ahash::AHashSet;
use directory::backend::internal::manage::ManageDirectory;
use futures::StreamExt;
use jmap_client::{
client_ws::WebSocketMessage,
@ -20,7 +19,10 @@ use std::time::Duration;
use tokio::sync::mpsc;
use crate::jmap::{assert_is_empty, mailbox::destroy_all_mailboxes, test_account_login};
use crate::{
directory::internal::TestInternalDirectory,
jmap::{assert_is_empty, mailbox::destroy_all_mailboxes, test_account_login},
};
use super::JMAPTest;
@ -29,18 +31,18 @@ pub async fn test(params: &mut JMAPTest) {
let server = params.server.clone();
// Authenticate all accounts
params
.directory
.create_test_user_with_email("jdoe@example.com", "12345", "John Doe")
.await;
let account_id = Id::from(
server
.core
.storage
.data
.get_or_create_principal_id("jdoe@example.com", directory::Type::Individual)
.await
.unwrap(),
.create_test_user(
"jdoe@example.com",
"12345",
"John Doe",
&["jdoe@example.com"],
)
.await,
)
.to_string();
let client = test_account_login("jdoe@example.com", "12345").await;