Directory implementation - part 3

This commit is contained in:
Mauro D
2023-06-01 17:08:48 +00:00
parent beffa408e6
commit 93e925a635
18 changed files with 1212 additions and 139 deletions

View File

@@ -1,12 +1,221 @@
use directory::Directory;
use directory::{Directory, Principal, Type};
use jmap_proto::types::id::Id;
use mail_send::Credentials;
use crate::directory::parse_config;
#[tokio::test]
async fn sql_directory() {
// Enable logging
/*tracing::subscriber::set_global_default(
tracing_subscriber::FmtSubscriber::builder()
.with_max_level(tracing::Level::DEBUG)
.finish(),
)
.unwrap();*/
// Obtain directory handle
let handle = parse_config().directories.remove("sql").unwrap();
// Create tables
create_test_directory(handle.as_ref()).await;
// Create test users
create_test_user(handle.as_ref(), "john", "12345", "John Doe").await;
create_test_user(handle.as_ref(), "jane", "abcde", "Jane Doe").await;
create_test_user(
handle.as_ref(),
"bill",
"$2y$05$bvIG6Nmid91Mu9RcmmWZfO5HJIMCT8riNW0hEp8f6/FuA2/mHZFpe",
"Bill Foobar",
)
.await;
set_test_quota(handle.as_ref(), "bill", 500000).await;
// Create test groups
create_test_group(handle.as_ref(), "sales", "Sales Team").await;
create_test_group(handle.as_ref(), "support", "Support Team").await;
// Link users to groups
add_to_group(handle.as_ref(), "john", "sales").await;
add_to_group(handle.as_ref(), "jane", "sales").await;
add_to_group(handle.as_ref(), "jane", "support").await;
// Add email addresses
link_test_address(handle.as_ref(), "john", "john@example.org", "primary").await;
link_test_address(handle.as_ref(), "jane", "jane@example.org", "primary").await;
link_test_address(handle.as_ref(), "bill", "bill@example.org", "primary").await;
// Add aliases and lists
link_test_address(handle.as_ref(), "john", "john.doe@example.org", "alias").await;
link_test_address(handle.as_ref(), "john", "jdoe@example.org", "alias").await;
link_test_address(handle.as_ref(), "john", "info@example.org", "list").await;
link_test_address(handle.as_ref(), "jane", "info@example.org", "list").await;
link_test_address(handle.as_ref(), "bill", "info@example.org", "list").await;
// Test authentication
assert_eq!(
handle
.authenticate(&Credentials::Plain {
username: "john".to_string(),
secret: "12345".to_string()
})
.await
.unwrap()
.unwrap(),
Principal {
id: 2,
name: "john".to_string(),
description: "John Doe".to_string().into(),
secrets: vec!["12345".to_string()],
typ: Type::Individual,
..Default::default()
}
);
assert_eq!(
handle
.authenticate(&Credentials::Plain {
username: "bill".to_string(),
secret: "password".to_string()
})
.await
.unwrap()
.unwrap(),
Principal {
id: 4,
name: "bill".to_string(),
description: "Bill Foobar".to_string().into(),
secrets: vec![
"$2y$05$bvIG6Nmid91Mu9RcmmWZfO5HJIMCT8riNW0hEp8f6/FuA2/mHZFpe".to_string()
],
typ: Type::Individual,
quota: 500000,
..Default::default()
}
);
assert!(handle
.authenticate(&Credentials::Plain {
username: "bill".to_string(),
secret: "invalid".to_string()
})
.await
.unwrap()
.is_none());
// Get by id
assert_eq!(
handle.principal_by_id(2).await.unwrap().unwrap(),
Principal {
id: 2,
name: "john".to_string(),
description: "John Doe".to_string().into(),
typ: Type::Individual,
..Default::default()
}
);
// Get user by name
assert_eq!(
handle.principal_by_name("jane").await.unwrap().unwrap(),
Principal {
id: 3,
name: "jane".to_string(),
description: "Jane Doe".to_string().into(),
typ: Type::Individual,
..Default::default()
}
);
// Get group by name
assert_eq!(
handle.principal_by_name("sales").await.unwrap().unwrap(),
Principal {
id: 5,
name: "sales".to_string(),
description: "Sales Team".to_string().into(),
typ: Type::Group,
..Default::default()
}
);
// Member of
assert_eq!(
handle
.member_of(&handle.principal_by_name("john").await.unwrap().unwrap())
.await
.unwrap(),
vec![5]
);
assert_eq!(
handle
.member_of(&handle.principal_by_name("jane").await.unwrap().unwrap())
.await
.unwrap(),
vec![5, 6]
);
// Emails by id
assert_eq!(
handle.emails_by_id(2).await.unwrap(),
vec![
"john@example.org".to_string(),
"jdoe@example.org".to_string(),
"john.doe@example.org".to_string(),
]
);
assert_eq!(
handle.emails_by_id(4).await.unwrap(),
vec!["bill@example.org".to_string(),]
);
// Ids by email
assert_eq!(
handle.ids_by_email("jane@example.org").await.unwrap(),
vec![3]
);
assert_eq!(
handle.ids_by_email("info@example.org").await.unwrap(),
vec![2, 3, 4]
);
// RCPT TO
assert!(handle.rcpt("jane@example.org").await.unwrap());
assert!(handle.rcpt("info@example.org").await.unwrap());
assert!(!handle.rcpt("invalid@example.org").await.unwrap());
// VRFY
assert_eq!(
handle.vrfy("jane").await.unwrap(),
vec!["jane@example.org".to_string()]
);
assert_eq!(
handle.vrfy("john").await.unwrap(),
vec!["john@example.org".to_string()]
);
assert_eq!(handle.vrfy("info").await.unwrap(), Vec::<String>::new());
assert_eq!(handle.vrfy("invalid").await.unwrap(), Vec::<String>::new());
// EXPN
assert_eq!(
handle.expn("info@example.org").await.unwrap(),
vec![
"bill@example.org".to_string(),
"jane@example.org".to_string(),
"john@example.org".to_string()
]
);
assert_eq!(
handle.expn("john@example.org").await.unwrap(),
Vec::<String>::new()
);
}
pub async fn create_test_directory(handle: &dyn Directory) {
// Create tables
for query in [
"CREATE TABLE accounts (name TEXT, id INTEGER PRIMARY KEY, secret TEXT, description TEXT, type TEXT NOT NULL, quota INTEGER, active BOOLEAN DEFAULT 1)",
"CREATE TABLE accounts (name TEXT, id INTEGER PRIMARY KEY, secret TEXT, description TEXT, type TEXT NOT NULL, quota INTEGER DEFAULT 0, active BOOLEAN DEFAULT 1)",
"CREATE TABLE group_members (uid INTEGER, gid INTEGER, PRIMARY KEY (uid, gid))",
"CREATE TABLE emails (id INTEGER NOT NULL, email TEXT NOT NULL, type TEXT, PRIMARY KEY (id, email))",
"CREATE TABLE emails (id INTEGER NOT NULL, address TEXT NOT NULL, type TEXT, PRIMARY KEY (id, address))",
"INSERT INTO accounts (name, secret, type) VALUES ('admin', 'secret', 'individual')",
] {
handle.query(query, &[]).await.unwrap_or_else(|_| panic!("failed for {query}"));
@@ -16,13 +225,13 @@ pub async fn create_test_directory(handle: &dyn Directory) {
pub async fn create_test_user(handle: &dyn Directory, login: &str, secret: &str, name: &str) -> Id {
handle
.query(
"INSERT OR IGNORE INTO users (name, secret, description, type, is_active) VALUES (?, ?, ?, 'individual', true)",
"INSERT OR IGNORE INTO accounts (name, secret, description, type, active) VALUES (?, ?, ?, 'individual', true)",
&[login, secret, name],
)
.await
.unwrap();
Id::from(handle.principal_by_name(login).await.unwrap().unwrap().id)
Id::from(get_principal_id(handle, login).await)
}
pub async fn create_test_user_with_email(
@@ -39,34 +248,27 @@ pub async fn create_test_user_with_email(
pub async fn create_test_group(handle: &dyn Directory, login: &str, name: &str) -> Id {
handle
.query(
"INSERT OR IGNORE INTO users (name, description, type, is_active) VALUES (?, ?, 'group', true)",
"INSERT OR IGNORE INTO accounts (name, description, type, active) VALUES (?, ?, 'group', true)",
&[login, name],
)
.await
.unwrap();
let id = handle.principal_by_name(login).await.unwrap().unwrap().id;
Id::from(get_principal_id(handle, login).await)
}
handle
.query(
&format!(
"INSERT OR IGNORE INTO emails (id, email, type) VALUES ({}, ?, 'primary')",
id
),
&[login],
)
.await
.unwrap();
Id::from(id)
pub async fn create_test_group_with_email(handle: &dyn Directory, login: &str, name: &str) -> Id {
let id = create_test_group(handle, login, name).await;
link_test_address(handle, login, login, "primary").await;
id
}
pub async fn link_test_address(handle: &dyn Directory, login: &str, address: &str, typ: &str) {
let id = handle.principal_by_name(login).await.unwrap().unwrap().id;
let id = get_principal_id(handle, login).await;
handle
.query(
&format!(
"INSERT OR IGNORE INTO emails (id, email, type) VALUES ({}, ?, ?)",
"INSERT OR IGNORE INTO emails (id, address, type) VALUES ({}, ?, ?)",
id,
),
&[address, typ],
@@ -75,7 +277,28 @@ pub async fn link_test_address(handle: &dyn Directory, login: &str, address: &st
.unwrap();
}
pub async fn add_to_group(handle: &dyn Directory, uid: u32, gid: u32) {
pub async fn set_test_quota(handle: &dyn Directory, login: &str, quota: u32) {
let id = get_principal_id(handle, login).await;
handle
.query(
&format!("UPDATE accounts SET quota = {} where id = {}", quota, id,),
&[],
)
.await
.unwrap();
}
pub async fn add_to_group(handle: &dyn Directory, login: &str, group: &str) {
let user = handle.principal_by_name(login).await.unwrap().unwrap();
let group = handle.principal_by_name(group).await.unwrap().unwrap();
let uid = user.id;
let gid = group.id;
assert_ne!(uid, gid, "{user:?} {group:?}");
assert_ne!(uid, u32::MAX, "{user:?} {group:?}");
assert_ne!(gid, u32::MAX, "{user:?} {group:?}");
handle
.query(
&format!(
@@ -99,12 +322,18 @@ pub async fn remove_from_group(handle: &dyn Directory, uid: u32, gid: u32) {
}
pub async fn remove_test_alias(handle: &dyn Directory, login: &str, alias: &str) {
let id = handle.principal_by_name(login).await.unwrap().unwrap().id;
let id = get_principal_id(handle, login).await;
handle
.query(
&format!("DELETE FROM emails WHERE id = {} AND email = ?", id),
&format!("DELETE FROM emails WHERE id = {} AND address = ?", id),
&[alias],
)
.await
.unwrap();
}
async fn get_principal_id(handle: &dyn Directory, name: &str) -> u32 {
let p = handle.principal_by_name(name).await.unwrap().unwrap();
assert_ne!(p.id, u32::MAX, "{name} {p:#?}");
p.id
}