mirror of
https://github.com/stalwartlabs/mail-server.git
synced 2024-11-10 09:32:19 +08:00
Automatically create Inbox for group accounts
This commit is contained in:
parent
a0ceaeba1c
commit
80f6a5469a
8 changed files with 128 additions and 51 deletions
|
@ -73,43 +73,8 @@ impl MemoryDirectory {
|
|||
member_of,
|
||||
},
|
||||
);
|
||||
let mut emails = Vec::new();
|
||||
for (pos, (_, email)) in config
|
||||
.values((prefix.as_str(), "users", lookup_id, "email"))
|
||||
.enumerate()
|
||||
{
|
||||
directory
|
||||
.emails_to_names
|
||||
.entry(email.to_string())
|
||||
.or_default()
|
||||
.push(if pos > 0 {
|
||||
EmailType::Alias(name.clone())
|
||||
} else {
|
||||
EmailType::Primary(name.clone())
|
||||
});
|
||||
|
||||
if let Some((_, domain)) = email.rsplit_once('@') {
|
||||
directory.domains.insert(domain.to_lowercase());
|
||||
}
|
||||
|
||||
emails.push(if pos > 0 {
|
||||
EmailType::Alias(email.to_lowercase())
|
||||
} else {
|
||||
EmailType::Primary(email.to_lowercase())
|
||||
});
|
||||
}
|
||||
for (_, email) in config.values((prefix.as_str(), "users", lookup_id, "email-list")) {
|
||||
directory
|
||||
.emails_to_names
|
||||
.entry(email.to_lowercase())
|
||||
.or_default()
|
||||
.push(EmailType::List(name.clone()));
|
||||
if let Some((_, domain)) = email.rsplit_once('@') {
|
||||
directory.domains.insert(domain.to_lowercase());
|
||||
}
|
||||
emails.push(EmailType::List(email.to_lowercase()));
|
||||
}
|
||||
directory.names_to_email.insert(name, emails);
|
||||
directory.parse_emails(config, (prefix.as_str(), "users", lookup_id), name)?;
|
||||
}
|
||||
|
||||
for lookup_id in config.sub_keys((prefix.as_str(), "groups")) {
|
||||
|
@ -119,7 +84,7 @@ impl MemoryDirectory {
|
|||
directory.principals.insert(
|
||||
name.clone(),
|
||||
Principal {
|
||||
name,
|
||||
name: name.clone(),
|
||||
secrets: vec![],
|
||||
typ: Type::Group,
|
||||
description: config
|
||||
|
@ -134,6 +99,8 @@ impl MemoryDirectory {
|
|||
.collect(),
|
||||
},
|
||||
);
|
||||
|
||||
directory.parse_emails(config, (prefix.as_str(), "groups", lookup_id), name)?;
|
||||
}
|
||||
|
||||
directory
|
||||
|
@ -143,3 +110,49 @@ impl MemoryDirectory {
|
|||
Ok(Arc::new(directory))
|
||||
}
|
||||
}
|
||||
|
||||
impl MemoryDirectory {
|
||||
fn parse_emails(
|
||||
&mut self,
|
||||
config: &Config,
|
||||
prefix: impl AsKey,
|
||||
name: String,
|
||||
) -> utils::config::Result<()> {
|
||||
let prefix = prefix.as_key();
|
||||
let mut emails = Vec::new();
|
||||
|
||||
for (pos, (_, email)) in config.values((prefix.as_str(), "email")).enumerate() {
|
||||
self.emails_to_names
|
||||
.entry(email.to_string())
|
||||
.or_default()
|
||||
.push(if pos > 0 {
|
||||
EmailType::Alias(name.clone())
|
||||
} else {
|
||||
EmailType::Primary(name.clone())
|
||||
});
|
||||
|
||||
if let Some((_, domain)) = email.rsplit_once('@') {
|
||||
self.domains.insert(domain.to_lowercase());
|
||||
}
|
||||
|
||||
emails.push(if pos > 0 {
|
||||
EmailType::Alias(email.to_lowercase())
|
||||
} else {
|
||||
EmailType::Primary(email.to_lowercase())
|
||||
});
|
||||
}
|
||||
for (_, email) in config.values((prefix.as_str(), "email-list")) {
|
||||
self.emails_to_names
|
||||
.entry(email.to_lowercase())
|
||||
.or_default()
|
||||
.push(EmailType::List(name.clone()));
|
||||
if let Some((_, domain)) = email.rsplit_once('@') {
|
||||
self.domains.insert(domain.to_lowercase());
|
||||
}
|
||||
emails.push(EmailType::List(email.to_lowercase()));
|
||||
}
|
||||
|
||||
self.names_to_email.insert(name, emails);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ use crate::{DirectoryOptions, Principal};
|
|||
pub mod config;
|
||||
pub mod lookup;
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(Default, Debug)]
|
||||
pub struct MemoryDirectory {
|
||||
principals: AHashMap<String, Principal>,
|
||||
emails_to_names: AHashMap<String, Vec<EmailType>>,
|
||||
|
@ -37,6 +37,7 @@ pub struct MemoryDirectory {
|
|||
opt: DirectoryOptions,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum EmailType {
|
||||
Primary(String),
|
||||
Alias(String),
|
||||
|
|
|
@ -81,17 +81,13 @@ impl SessionData {
|
|||
mailbox_prefix: Option<String>,
|
||||
access_token: &AccessToken,
|
||||
) -> crate::Result<Account> {
|
||||
let mailbox_ids = if access_token.is_primary_id(account_id) {
|
||||
let mailbox_ids = if access_token.is_primary_id(account_id)
|
||||
|| access_token.member_of.contains(&account_id)
|
||||
{
|
||||
self.jmap
|
||||
.mailbox_get_or_create(account_id)
|
||||
.await
|
||||
.map_err(|_| {})?
|
||||
} else if access_token.member_of.contains(&account_id) {
|
||||
self.jmap
|
||||
.get_document_ids(account_id, Collection::Mailbox)
|
||||
.await
|
||||
.map_err(|_| {})?
|
||||
.unwrap_or_default()
|
||||
} else {
|
||||
self.jmap
|
||||
.shared_documents(access_token, account_id, Collection::Mailbox, Acl::Read)
|
||||
|
|
|
@ -57,8 +57,7 @@ pub fn spawn_writer(mut stream: Event, span: tracing::Span) -> mpsc::Sender<Even
|
|||
data = std::str::from_utf8(bytes.as_ref()).unwrap_or_default(),
|
||||
size = bytes.len()
|
||||
);
|
||||
/*let tmp = "dd";
|
||||
println!(
|
||||
/*let c = println!(
|
||||
"<- {:?}",
|
||||
String::from_utf8_lossy(
|
||||
&bytes[..std::cmp::min(bytes.len(), 100)]
|
||||
|
|
|
@ -176,10 +176,12 @@ email-list = ["info@example.org"]
|
|||
|
||||
[[directory."local".groups]]
|
||||
name = "sales"
|
||||
email = "sales@example.org"
|
||||
description = "Sales Team"
|
||||
|
||||
[[directory."local".groups]]
|
||||
name = "support"
|
||||
email = "support@example.org"
|
||||
description = "Support Team"
|
||||
|
||||
[oauth]
|
||||
|
|
|
@ -23,9 +23,27 @@
|
|||
|
||||
use imap_proto::ResponseType;
|
||||
|
||||
use crate::jmap::delivery::SmtpConnection;
|
||||
|
||||
use super::{append::assert_append_message, AssertResult, ImapConnection, Type};
|
||||
|
||||
pub async fn test(mut imap_john: &mut ImapConnection, _imap_check: &mut ImapConnection) {
|
||||
// Delivery to support account
|
||||
let mut lmtp = SmtpConnection::connect_port(11201).await;
|
||||
lmtp.ingest(
|
||||
"bill@example.com",
|
||||
&["support@example.com"],
|
||||
concat!(
|
||||
"From: bill@example.com\r\n",
|
||||
"To: support@example.com\r\n",
|
||||
"Subject: TPS Report\r\n",
|
||||
"\r\n",
|
||||
"I'm going to need those TPS reports ASAP. ",
|
||||
"So, if you could do that, that'd be great."
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
// Connect to all test accounts
|
||||
let mut imap_jane = ImapConnection::connect(b"_w ").await;
|
||||
let mut imap_bill = ImapConnection::connect(b"_z ").await;
|
||||
|
@ -43,6 +61,25 @@ pub async fn test(mut imap_john: &mut ImapConnection, _imap_check: &mut ImapConn
|
|||
imap.assert_read(Type::Tagged, ResponseType::Ok).await;
|
||||
}
|
||||
|
||||
// Jane should see the Support account
|
||||
imap_jane.send("LIST \"\" \"*\"").await;
|
||||
imap_jane
|
||||
.assert_read(Type::Tagged, ResponseType::Ok)
|
||||
.await
|
||||
.assert_contains("Shared Folders/support@example.com/Inbox");
|
||||
|
||||
imap_jane
|
||||
.send("SELECT \"Shared Folders/support@example.com/Inbox\"")
|
||||
.await;
|
||||
imap_jane.assert_read(Type::Tagged, ResponseType::Ok).await;
|
||||
imap_jane.send("FETCH 1 (PREVIEW)").await;
|
||||
imap_jane
|
||||
.assert_read(Type::Tagged, ResponseType::Ok)
|
||||
.await
|
||||
.assert_contains("TPS reports ASAP");
|
||||
imap_jane.send("UNSELECT").await;
|
||||
imap_jane.assert_read(Type::Tagged, ResponseType::Ok).await;
|
||||
|
||||
// John should have no shared folders
|
||||
imap_john.send("LIST \"\" \"*\"").await;
|
||||
imap_john
|
||||
|
|
|
@ -42,7 +42,7 @@ use directory::config::ConfigDirectory;
|
|||
use imap::core::{ImapSessionManager, IMAP};
|
||||
use imap_proto::ResponseType;
|
||||
use jmap::{api::JmapSessionManager, services::IPC_CHANNEL_BUFFER, JMAP};
|
||||
use smtp::core::SMTP;
|
||||
use smtp::core::{SmtpSessionManager, SMTP};
|
||||
use tokio::{
|
||||
io::{AsyncBufReadExt, AsyncWriteExt, BufReader, Lines, ReadHalf, WriteHalf},
|
||||
net::TcpStream,
|
||||
|
@ -53,7 +53,8 @@ use utils::{config::ServerProtocol, UnwrapFailure};
|
|||
use crate::{
|
||||
add_test_certs,
|
||||
directory::sql::{
|
||||
add_to_group, create_test_directory, create_test_user, create_test_user_with_email,
|
||||
add_to_group, create_test_directory, create_test_group_with_email, create_test_user,
|
||||
create_test_user_with_email,
|
||||
},
|
||||
store::TempDir,
|
||||
};
|
||||
|
@ -79,6 +80,12 @@ protocol = "managesieve"
|
|||
max-connections = 81920
|
||||
tls.implicit = true
|
||||
|
||||
[server.listener.lmtp-debug]
|
||||
bind = ['127.0.0.1:11201']
|
||||
greeting = 'Test LMTP instance'
|
||||
protocol = 'lmtp'
|
||||
tls.implicit = false
|
||||
|
||||
[server.socket]
|
||||
reuse-addr = true
|
||||
|
||||
|
@ -260,6 +267,9 @@ async fn init_imap_tests(delete_if_exists: bool) -> IMAPTest {
|
|||
ManageSieveSessionManager::new(jmap.clone(), imap.clone()),
|
||||
shutdown_rx,
|
||||
),
|
||||
ServerProtocol::Smtp | ServerProtocol::Lmtp => {
|
||||
server.spawn(SmtpSessionManager::new(smtp.clone()), shutdown_rx)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
});
|
||||
|
@ -289,6 +299,18 @@ async fn init_imap_tests(delete_if_exists: bool) -> IMAPTest {
|
|||
"Bill Foobar",
|
||||
)
|
||||
.await;
|
||||
create_test_group_with_email(
|
||||
jmap.directory.as_ref(),
|
||||
"support@example.com",
|
||||
"Support Group",
|
||||
)
|
||||
.await;
|
||||
add_to_group(
|
||||
jmap.directory.as_ref(),
|
||||
"jane.smith@example.com",
|
||||
"support@example.com",
|
||||
)
|
||||
.await;
|
||||
|
||||
if delete_if_exists {
|
||||
jmap.store.destroy().await;
|
||||
|
|
|
@ -295,8 +295,15 @@ impl SmtpConnection {
|
|||
}
|
||||
|
||||
pub async fn connect() -> Self {
|
||||
let (reader, writer) =
|
||||
tokio::io::split(TcpStream::connect("127.0.0.1:11200").await.unwrap());
|
||||
SmtpConnection::connect_port(11200).await
|
||||
}
|
||||
|
||||
pub async fn connect_port(port: u16) -> Self {
|
||||
let (reader, writer) = tokio::io::split(
|
||||
TcpStream::connect(&format!("127.0.0.1:{port}"))
|
||||
.await
|
||||
.unwrap(),
|
||||
);
|
||||
let mut conn = SmtpConnection {
|
||||
reader: BufReader::new(reader).lines(),
|
||||
writer,
|
||||
|
|
Loading…
Reference in a new issue