mirror of
https://github.com/stalwartlabs/stalwart.git
synced 2026-11-06 16:14:11 +00:00
CLI account management + Directory refactoring
This commit is contained in:
@@ -22,8 +22,11 @@
|
||||
*/
|
||||
|
||||
use directory::{
|
||||
backend::internal::{manage::ManageDirectory, PrincipalField, PrincipalUpdate, PrincipalValue},
|
||||
Directory, DirectoryError, ManagementError, Principal, QueryBy, Type,
|
||||
backend::internal::{
|
||||
lookup::DirectoryStore, manage::ManageDirectory, PrincipalField, PrincipalUpdate,
|
||||
PrincipalValue,
|
||||
},
|
||||
DirectoryError, ManagementError, Principal, QueryBy, Type,
|
||||
};
|
||||
use jmap_proto::types::collection::Collection;
|
||||
use mail_send::Credentials;
|
||||
@@ -72,9 +75,10 @@ async fn internal_directory() {
|
||||
..Default::default()
|
||||
})
|
||||
.await,
|
||||
Err(DirectoryError::Management(ManagementError::NotUniqueField(
|
||||
PrincipalField::Name
|
||||
)))
|
||||
Err(DirectoryError::Management(ManagementError::AlreadyExists {
|
||||
field: PrincipalField::Name,
|
||||
value: "john".to_string()
|
||||
}))
|
||||
);
|
||||
|
||||
// An account using a non-existent domain should fail
|
||||
@@ -190,9 +194,10 @@ async fn internal_directory() {
|
||||
..Default::default()
|
||||
})
|
||||
.await,
|
||||
Err(DirectoryError::Management(ManagementError::NotUniqueField(
|
||||
PrincipalField::Emails
|
||||
)))
|
||||
Err(DirectoryError::Management(ManagementError::AlreadyExists {
|
||||
field: PrincipalField::Emails,
|
||||
value: "jane@example.org".to_string()
|
||||
}))
|
||||
);
|
||||
|
||||
// Create a mailing list
|
||||
@@ -458,9 +463,10 @@ async fn internal_directory() {
|
||||
),],
|
||||
)
|
||||
.await,
|
||||
Err(DirectoryError::Management(ManagementError::NotUniqueField(
|
||||
PrincipalField::Name
|
||||
)))
|
||||
Err(DirectoryError::Management(ManagementError::AlreadyExists {
|
||||
field: PrincipalField::Name,
|
||||
value: "jane".to_string()
|
||||
}))
|
||||
);
|
||||
assert_eq!(
|
||||
store
|
||||
@@ -472,20 +478,42 @@ async fn internal_directory() {
|
||||
),],
|
||||
)
|
||||
.await,
|
||||
Err(DirectoryError::Management(ManagementError::NotUniqueField(
|
||||
PrincipalField::Emails
|
||||
)))
|
||||
Err(DirectoryError::Management(ManagementError::AlreadyExists {
|
||||
field: PrincipalField::Emails,
|
||||
value: "jane@example.org".to_string()
|
||||
}))
|
||||
);
|
||||
|
||||
// List accounts
|
||||
assert_eq!(
|
||||
store.list_accounts(None, 0).await.unwrap(),
|
||||
store.list_accounts(None, None, 0).await.unwrap(),
|
||||
vec!["jane", "john.doe", "list", "sales", "support"]
|
||||
);
|
||||
assert_eq!(
|
||||
store.list_accounts("john".into(), 2).await.unwrap(),
|
||||
store.list_accounts("john".into(), None, 2).await.unwrap(),
|
||||
vec!["john.doe", "list"]
|
||||
);
|
||||
assert_eq!(
|
||||
store
|
||||
.list_accounts(None, Type::Individual.into(), 0)
|
||||
.await
|
||||
.unwrap(),
|
||||
vec!["jane", "john.doe"]
|
||||
);
|
||||
assert_eq!(
|
||||
store
|
||||
.list_accounts(None, Type::Group.into(), 0)
|
||||
.await
|
||||
.unwrap(),
|
||||
vec!["sales", "support"]
|
||||
);
|
||||
assert_eq!(
|
||||
store
|
||||
.list_accounts(None, Type::List.into(), 0)
|
||||
.await
|
||||
.unwrap(),
|
||||
vec!["list"]
|
||||
);
|
||||
|
||||
// Write records on John's and Jane's accounts
|
||||
for account_id in [0, 1] {
|
||||
@@ -527,7 +555,7 @@ async fn internal_directory() {
|
||||
);
|
||||
assert!(!store.rcpt("john.doe@example.org").await.unwrap());
|
||||
assert_eq!(
|
||||
store.list_accounts(None, 0).await.unwrap(),
|
||||
store.list_accounts(None, None, 0).await.unwrap(),
|
||||
vec!["jane", "list", "sales", "support"]
|
||||
);
|
||||
assert_eq!(
|
||||
|
||||
@@ -29,7 +29,7 @@ pub mod sql;
|
||||
|
||||
use ::smtp::core::Lookup;
|
||||
use directory::{
|
||||
backend::internal::manage::ManageDirectory, config::ConfigDirectory, AddressMapping,
|
||||
backend::internal::manage::ManageDirectory, core::config::ConfigDirectory, AddressMapping,
|
||||
Directories, Principal,
|
||||
};
|
||||
use mail_send::Credentials;
|
||||
@@ -59,6 +59,13 @@ email = "address"
|
||||
quota = "quota"
|
||||
type = "type"
|
||||
|
||||
[store."rocksdb"]
|
||||
type = "rocksdb"
|
||||
path = "{TMP}/rocksdb"
|
||||
|
||||
[store."foundationdb"]
|
||||
type = "foundationdb"
|
||||
|
||||
[store."sqlite"]
|
||||
type = "sqlite"
|
||||
path = "{TMP}/auth.db"
|
||||
@@ -153,7 +160,7 @@ base-dn = "dc=example,dc=org"
|
||||
dn = "cn=serviceuser,ou=svcaccts,dc=example,dc=org"
|
||||
secret = "mysecret"
|
||||
|
||||
[directory."ldap".auth-bind]
|
||||
[directory."ldap".bind.auth]
|
||||
enable = false
|
||||
dn = "cn=?,ou=svcaccts,dc=example,dc=org"
|
||||
|
||||
@@ -278,7 +285,11 @@ pub struct DirectoryTest {
|
||||
impl DirectoryTest {
|
||||
pub async fn new(id_store: Option<&str>) -> DirectoryTest {
|
||||
let temp_dir = TempDir::new("directory_tests", true);
|
||||
let config_file = CONFIG.replace("{TMP}", &temp_dir.path.to_string_lossy());
|
||||
let mut config_file = CONFIG.replace("{TMP}", &temp_dir.path.to_string_lossy());
|
||||
if id_store.is_some() {
|
||||
// Disable foundationdb store for SQL tests (the fdb select api version can only be run once per process)
|
||||
config_file = config_file.replace("foundationdb", "ignore");
|
||||
}
|
||||
let config = utils::config::Config::new(&config_file).unwrap();
|
||||
let stores = config.parse_stores().await.unwrap();
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ use std::{path::PathBuf, sync::Arc, time::Duration};
|
||||
use ::managesieve::core::ManageSieveSessionManager;
|
||||
use ::store::config::ConfigStore;
|
||||
use ahash::AHashSet;
|
||||
use directory::{backend::internal::manage::ManageDirectory, config::ConfigDirectory};
|
||||
use directory::{backend::internal::manage::ManageDirectory, core::config::ConfigDirectory};
|
||||
use imap::core::{ImapSessionManager, IMAP};
|
||||
use imap_proto::ResponseType;
|
||||
use jmap::{api::JmapSessionManager, services::IPC_CHANNEL_BUFFER, JMAP};
|
||||
@@ -162,6 +162,8 @@ type = "elasticsearch"
|
||||
url = "https://localhost:9200"
|
||||
user = "elastic"
|
||||
password = "RtQ-Lu6+o4rxx=XJplVJ"
|
||||
|
||||
[store."elastic".tls]
|
||||
allow-invalid-certs = true
|
||||
|
||||
[certificate.default]
|
||||
|
||||
@@ -45,7 +45,7 @@ pub async fn test(params: &mut JMAPTest) {
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
server.store.blob_hash_expire_all().await;
|
||||
server.store.blob_expire_all().await;
|
||||
|
||||
// Blob/set simple test
|
||||
let response = jmap_json_request(
|
||||
@@ -195,7 +195,7 @@ pub async fn test(params: &mut JMAPTest) {
|
||||
);
|
||||
}
|
||||
|
||||
server.store.blob_hash_expire_all().await;
|
||||
server.store.blob_expire_all().await;
|
||||
|
||||
// Blob/upload Complex Example
|
||||
let response = jmap_json_request(
|
||||
@@ -289,7 +289,7 @@ pub async fn test(params: &mut JMAPTest) {
|
||||
"Pointer {pointer:?} Response: {response:?}",
|
||||
);
|
||||
}
|
||||
server.store.blob_hash_expire_all().await;
|
||||
server.store.blob_expire_all().await;
|
||||
|
||||
// Blob/get Example with Range and Encoding Errors
|
||||
let response = jmap_json_request(
|
||||
@@ -428,7 +428,7 @@ pub async fn test(params: &mut JMAPTest) {
|
||||
"Pointer {pointer:?} Response: {response:?}",
|
||||
);
|
||||
}
|
||||
server.store.blob_hash_expire_all().await;
|
||||
server.store.blob_expire_all().await;
|
||||
|
||||
// Blob/lookup
|
||||
params.client.set_default_account_id(account_id.to_string());
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
use std::{sync::Arc, time::Duration};
|
||||
|
||||
use base64::{engine::general_purpose, Engine};
|
||||
use directory::config::ConfigDirectory;
|
||||
use directory::core::config::ConfigDirectory;
|
||||
use jmap::{
|
||||
api::JmapSessionManager,
|
||||
services::{housekeeper::Event, IPC_CHANNEL_BUFFER},
|
||||
|
||||
@@ -70,7 +70,7 @@ pub async fn test(params: &mut JMAPTest) {
|
||||
.await;
|
||||
|
||||
// Delete temporary blobs from previous tests
|
||||
server.store.blob_hash_expire_all().await;
|
||||
server.store.blob_expire_all().await;
|
||||
|
||||
// Test temporary blob quota (3 files)
|
||||
DISABLE_UPLOAD_QUOTA.store(false, std::sync::atomic::Ordering::Relaxed);
|
||||
@@ -93,7 +93,7 @@ pub async fn test(params: &mut JMAPTest) {
|
||||
jmap_client::Error::Problem(err) if err.detail().unwrap().contains("quota") => (),
|
||||
other => panic!("Unexpected error: {:?}", other),
|
||||
}
|
||||
server.store.blob_hash_expire_all().await;
|
||||
server.store.blob_expire_all().await;
|
||||
|
||||
// Test temporary blob quota (50000 bytes)
|
||||
for i in 0..2 {
|
||||
@@ -114,7 +114,7 @@ pub async fn test(params: &mut JMAPTest) {
|
||||
jmap_client::Error::Problem(err) if err.detail().unwrap().contains("quota") => (),
|
||||
other => panic!("Unexpected error: {:?}", other),
|
||||
}
|
||||
server.store.blob_hash_expire_all().await;
|
||||
server.store.blob_expire_all().await;
|
||||
|
||||
// Test JMAP Quotas extension
|
||||
let response = jmap_raw_request(
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
use directory::config::ConfigDirectory;
|
||||
use directory::core::config::ConfigDirectory;
|
||||
use smtp_proto::{AUTH_LOGIN, AUTH_PLAIN};
|
||||
use store::Stores;
|
||||
use utils::config::{Config, DynValue};
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
use directory::config::ConfigDirectory;
|
||||
use directory::core::config::ConfigDirectory;
|
||||
use store::Stores;
|
||||
use utils::config::Config;
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ use std::{
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use directory::config::ConfigDirectory;
|
||||
use directory::core::config::ConfigDirectory;
|
||||
use mail_auth::{
|
||||
common::{parse::TxtRecordParser, verify::DomainKey},
|
||||
dkim::DomainKeyReport,
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use directory::config::ConfigDirectory;
|
||||
use directory::core::config::ConfigDirectory;
|
||||
use smtp_proto::{RCPT_NOTIFY_DELAY, RCPT_NOTIFY_FAILURE, RCPT_NOTIFY_SUCCESS};
|
||||
use store::Stores;
|
||||
use utils::config::Config;
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
*/
|
||||
|
||||
use crate::smtp::{inbound::sign::TextConfigContext, session::TestSession, TestConfig};
|
||||
use directory::config::ConfigDirectory;
|
||||
use directory::core::config::ConfigDirectory;
|
||||
use smtp::{
|
||||
config::{if_block::ConfigIf, scripts::ConfigSieve, ConfigContext, EnvelopeKey, IfBlock},
|
||||
core::{Session, SMTP},
|
||||
|
||||
@@ -29,7 +29,7 @@ use crate::smtp::{
|
||||
session::{TestSession, VerifyResponse},
|
||||
TestConfig, TestSMTP,
|
||||
};
|
||||
use directory::config::ConfigDirectory;
|
||||
use directory::core::config::ConfigDirectory;
|
||||
use smtp::{
|
||||
config::{scripts::ConfigSieve, session::ConfigSession, ConfigContext, EnvelopeKey, IfBlock},
|
||||
core::{Session, SMTP},
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use directory::config::ConfigDirectory;
|
||||
use directory::core::config::ConfigDirectory;
|
||||
use mail_auth::{
|
||||
common::{parse::TxtRecordParser, verify::DomainKey},
|
||||
spf::Spf,
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
use directory::config::ConfigDirectory;
|
||||
use directory::core::config::ConfigDirectory;
|
||||
use store::Stores;
|
||||
use utils::config::Config;
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use directory::config::ConfigDirectory;
|
||||
use directory::core::config::ConfigDirectory;
|
||||
use smtp_proto::{AUTH_LOGIN, AUTH_PLAIN};
|
||||
use store::config::ConfigStore;
|
||||
use utils::config::{Config, DynValue};
|
||||
|
||||
@@ -27,7 +27,7 @@ use std::{
|
||||
};
|
||||
|
||||
use ahash::{AHashMap, HashMap, HashSet};
|
||||
use directory::config::ConfigDirectory;
|
||||
use directory::core::config::ConfigDirectory;
|
||||
use mail_auth::MX;
|
||||
use mail_parser::DateTime;
|
||||
use reqwest::{header::AUTHORIZATION, StatusCode};
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use ahash::{AHashMap, HashSet};
|
||||
use directory::config::ConfigDirectory;
|
||||
use directory::core::config::ConfigDirectory;
|
||||
use mail_auth::{
|
||||
common::parse::TxtRecordParser,
|
||||
dmarc::Dmarc,
|
||||
|
||||
@@ -25,7 +25,7 @@ use std::{path::PathBuf, sync::Arc, time::Duration};
|
||||
|
||||
use ahash::AHashMap;
|
||||
use dashmap::DashMap;
|
||||
use directory::backend::memory::MemoryDirectory;
|
||||
use directory::Directory;
|
||||
use mail_auth::{
|
||||
common::lru::{DnsCache, LruCache},
|
||||
hickory_resolver::config::{ResolverConfig, ResolverOpts},
|
||||
@@ -342,7 +342,7 @@ impl TestConfig for QueueConfig {
|
||||
rcpt: vec![],
|
||||
rcpt_domain: vec![],
|
||||
},
|
||||
management_lookup: Arc::new(MemoryDirectory::default()),
|
||||
management_lookup: Arc::new(Directory::default()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user