Access token permissions

This commit is contained in:
mdecimus
2024-09-10 18:44:44 +02:00
parent 08a95ae58b
commit fbcf55d8e1
128 changed files with 2415 additions and 906 deletions

View File

@@ -21,7 +21,7 @@ use store::{
BitmapKey, ValueKey,
};
use crate::directory::DirectoryTest;
use crate::directory::{DirectoryTest, IntoTestPrincipal, TestPrincipal};
#[tokio::test]
async fn internal_directory() {
@@ -33,20 +33,20 @@ async fn internal_directory() {
// A principal without name should fail
assert_eq!(
store.create_account(Principal::default(), vec![]).await,
store.create_account(Principal::default()).await,
Err(manage::err_missing(PrincipalField::Name))
);
// Basic account creation
let john_id = store
.create_account(
Principal {
TestPrincipal {
name: "john".to_string(),
description: Some("John Doe".to_string()),
secrets: vec!["secret".to_string(), "secret2".to_string()],
..Default::default()
},
vec![],
}
.into(),
)
.await
.unwrap();
@@ -55,11 +55,11 @@ async fn internal_directory() {
assert_eq!(
store
.create_account(
Principal {
TestPrincipal {
name: "john".to_string(),
..Default::default()
},
vec![]
}
.into(),
)
.await,
Err(manage::err_exists(PrincipalField::Name, "john".to_string()))
@@ -69,12 +69,12 @@ async fn internal_directory() {
assert_eq!(
store
.create_account(
Principal {
TestPrincipal {
name: "jane".to_string(),
emails: vec!["jane@example.org".to_string()],
..Default::default()
},
vec![]
}
.into(),
)
.await,
Err(manage::not_found("example.org".to_string()))
@@ -121,15 +121,15 @@ async fn internal_directory() {
// Create an account with an email address
let jane_id = store
.create_account(
Principal {
TestPrincipal {
name: "jane".to_string(),
description: Some("Jane Doe".to_string()),
secrets: vec!["my_secret".to_string(), "my_secret2".to_string()],
emails: vec!["jane@example.org".to_string()],
quota: 123,
..Default::default()
},
vec![],
}
.into(),
)
.await
.unwrap();
@@ -151,8 +151,9 @@ async fn internal_directory() {
true
)
.await
.unwrap(),
Some(Principal {
.unwrap()
.map(|p| p.into_test()),
Some(TestPrincipal {
id: jane_id,
name: "jane".to_string(),
description: Some("Jane Doe".to_string()),
@@ -180,13 +181,13 @@ async fn internal_directory() {
assert_eq!(
store
.create_account(
Principal {
TestPrincipal {
name: "janeth".to_string(),
description: Some("Janeth Doe".to_string()),
emails: vec!["jane@example.org".to_string()],
..Default::default()
},
vec![]
}
.into()
)
.await,
Err(manage::err_exists(
@@ -198,13 +199,13 @@ async fn internal_directory() {
// Create a mailing list
let list_id = store
.create_account(
Principal {
TestPrincipal {
name: "list".to_string(),
typ: Type::List,
emails: vec!["list@example.org".to_string()],
..Default::default()
},
vec![],
}
.into(),
)
.await
.unwrap();
@@ -235,8 +236,9 @@ async fn internal_directory() {
.query(QueryBy::Name("list"), true)
.await
.unwrap()
.unwrap(),
Principal {
.unwrap()
.into_test(),
TestPrincipal {
name: "list".to_string(),
id: list_id,
typ: Type::List,
@@ -260,25 +262,25 @@ async fn internal_directory() {
// Create groups
store
.create_account(
Principal {
TestPrincipal {
name: "sales".to_string(),
description: Some("Sales Team".to_string()),
typ: Type::Group,
..Default::default()
},
vec![],
}
.into(),
)
.await
.unwrap();
store
.create_account(
Principal {
TestPrincipal {
name: "support".to_string(),
description: Some("Support Team".to_string()),
typ: Type::Group,
..Default::default()
},
vec![],
}
.into(),
)
.await
.unwrap();
@@ -313,8 +315,9 @@ async fn internal_directory() {
)
.await
.unwrap()
.into_test()
.into_sorted(),
Principal {
TestPrincipal {
id: john_id,
name: "john".to_string(),
description: Some("John Doe".to_string()),
@@ -367,8 +370,9 @@ async fn internal_directory() {
)
.await
.unwrap()
.into_test()
.into_sorted(),
Principal {
TestPrincipal {
id: john_id,
name: "john".to_string(),
description: Some("John Doe".to_string()),
@@ -398,10 +402,6 @@ async fn internal_directory() {
PrincipalValue::StringList(vec!["12345".to_string()])
),
PrincipalUpdate::set(PrincipalField::Quota, PrincipalValue::Integer(1024)),
PrincipalUpdate::set(
PrincipalField::Type,
PrincipalValue::String("superuser".to_string())
),
PrincipalUpdate::remove_item(
PrincipalField::Emails,
PrincipalValue::String("john@example.org".to_string()),
@@ -426,15 +426,16 @@ async fn internal_directory() {
)
.await
.unwrap()
.into_test()
.into_sorted(),
Principal {
TestPrincipal {
id: john_id,
name: "john.doe".to_string(),
description: Some("Johnny Doe".to_string()),
secrets: vec!["12345".to_string()],
emails: vec!["john.doe@example.org".to_string()],
quota: 1024,
typ: Type::Superuser,
typ: Type::Individual,
member_of: vec!["list".to_string(), "sales".to_string()],
}
);

View File

@@ -6,10 +6,10 @@
use std::fmt::Debug;
use directory::{backend::internal::manage::ManageDirectory, Principal, QueryBy, Type};
use directory::{backend::internal::manage::ManageDirectory, QueryBy, Type};
use mail_send::Credentials;
use crate::directory::{map_account_ids, DirectoryTest};
use crate::directory::{map_account_ids, DirectoryTest, IntoTestPrincipal, TestPrincipal};
#[tokio::test]
async fn ldap_directory() {
@@ -40,14 +40,19 @@ async fn ldap_directory() {
.await
.unwrap()
.unwrap()
.into_test()
.into_sorted(),
Principal {
TestPrincipal {
id: base_store.get_account_id("john").await.unwrap().unwrap(),
name: "john".to_string(),
description: "John Doe".to_string().into(),
secrets: vec!["12345".to_string()],
typ: Type::Individual,
member_of: map_account_ids(base_store, vec!["sales"]).await,
member_of: map_account_ids(base_store, vec!["sales"])
.await
.into_iter()
.map(|v| v.to_string())
.collect(),
emails: vec![
"john@example.org".to_string(),
"john.doe@example.org".to_string()
@@ -68,8 +73,9 @@ async fn ldap_directory() {
.await
.unwrap()
.unwrap()
.into_test()
.into_sorted(),
Principal {
TestPrincipal {
id: base_store.get_account_id("bill").await.unwrap().unwrap(),
name: "bill".to_string(),
description: "Bill Foobar".to_string().into(),
@@ -102,14 +108,19 @@ async fn ldap_directory() {
.await
.unwrap()
.unwrap()
.into_test()
.into_sorted(),
Principal {
TestPrincipal {
id: base_store.get_account_id("jane").await.unwrap().unwrap(),
name: "jane".to_string(),
description: "Jane Doe".to_string().into(),
typ: Type::Individual,
secrets: vec!["abcde".to_string()],
member_of: map_account_ids(base_store, vec!["sales", "support"]).await,
member_of: map_account_ids(base_store, vec!["sales", "support"])
.await
.into_iter()
.map(|v| v.to_string())
.collect(),
emails: vec!["jane@example.org".to_string(),],
..Default::default()
}
@@ -122,8 +133,9 @@ async fn ldap_directory() {
.query(QueryBy::Name("sales"), true)
.await
.unwrap()
.unwrap(),
Principal {
.unwrap()
.into_test(),
TestPrincipal {
id: base_store.get_account_id("sales").await.unwrap().unwrap(),
name: "sales".to_string(),
description: "sales".to_string().into(),

View File

@@ -11,7 +11,10 @@ pub mod smtp;
pub mod sql;
use common::{config::smtp::session::AddressMapping, Core};
use directory::{backend::internal::manage::ManageDirectory, Directories};
use directory::{
backend::internal::{manage::ManageDirectory, PrincipalField},
Directories, Principal, Type,
};
use mail_send::Credentials;
use rustls::ServerConfig;
use rustls_pemfile::{certs, pkcs8_private_keys};
@@ -254,6 +257,18 @@ pub struct DirectoryTest {
pub core: Core,
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct TestPrincipal {
pub id: u32,
pub typ: Type,
pub quota: u64,
pub name: String,
pub secrets: Vec<String>,
pub emails: Vec<String>,
pub member_of: Vec<String>,
pub description: Option<String>,
}
impl DirectoryTest {
pub async fn new(id_store: Option<&str>) -> DirectoryTest {
let temp_dir = TempDir::new("directory_tests", true);
@@ -408,6 +423,61 @@ pub fn dummy_tls_acceptor() -> Arc<TlsAcceptor> {
)))
}
trait IntoTestPrincipal {
fn into_test(self) -> TestPrincipal;
}
impl IntoTestPrincipal for Principal {
fn into_test(self) -> TestPrincipal {
TestPrincipal::from(self)
}
}
impl TestPrincipal {
pub fn into_sorted(mut self) -> Self {
self.member_of.sort_unstable();
self.emails.sort_unstable();
self
}
}
impl From<Principal> for TestPrincipal {
fn from(mut value: Principal) -> Self {
Self {
id: value.id(),
typ: value.typ(),
quota: value.quota(),
name: value.take_str(PrincipalField::Name).unwrap_or_default(),
secrets: value
.take_str_array(PrincipalField::Secrets)
.unwrap_or_default(),
emails: value
.take_str_array(PrincipalField::Emails)
.unwrap_or_default(),
member_of: value
.take_str_array(PrincipalField::MemberOf)
.unwrap_or_default(),
/*member_of: value
.iter_int(PrincipalField::MemberOf)
.map(|v| v as u32)
.collect(),*/
description: value.take_str(PrincipalField::Description),
}
}
}
impl From<TestPrincipal> for Principal {
fn from(value: TestPrincipal) -> Self {
Principal::new(value.id, value.typ)
.with_field(PrincipalField::Name, value.name)
.with_field(PrincipalField::Quota, value.quota)
.with_field(PrincipalField::Secrets, value.secrets)
.with_field(PrincipalField::Emails, value.emails)
.with_field(PrincipalField::MemberOf, value.member_of)
.with_opt_field(PrincipalField::Description, value.description)
}
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub enum Item {
IsAccount(String),
@@ -500,92 +570,6 @@ impl core::fmt::Debug for Item {
}
}
/*
// DEPRECATED - TODO: Remove
#[tokio::test(flavor = "multi_thread")]
#[ignore]
async fn lookup_local() {
const LOOKUP_CONFIG: &str = r#"
[store."local/regex"]
type = "memory"
format = "regex"
values = ["^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$",
"^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"]
[store."local/glob"]
type = "memory"
format = "glob"
values = ["*@example.org", "test@*", "localhost", "*+*@*.domain.net"]
[store."local/list"]
type = "memory"
format = "list"
values = ["abc", "xyz", "123"]
[store."local/suffix"]
type = "memory"
format = "glob"
comment = "//"
values = ["https://publicsuffix.org/list/public_suffix_list.dat", "fallback+file://%PATH%/public_suffix_list.dat.gz"]
"#;
/*tracing::subscriber::set_global_default(
tracing_subscriber::FmtSubscriber::builder()
.with_max_level(tracing::Level::TRACE)
.finish(),
)
.unwrap();*/
let mut config = utils::config::Config::new(
&LOOKUP_CONFIG.replace(
"%PATH%",
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.parent()
.unwrap()
.to_path_buf()
.join("resources")
.join("config")
.join("lists")
.to_str()
.unwrap(),
),
)
.unwrap();
let lookups = Stores::parse_all(&mut config).await.lookup_stores;
for (lookup, item, expect) in [
("glob", "user@example.org", true),
("glob", "test@otherdomain.org", true),
("glob", "localhost", true),
("glob", "john+doe@doefamily.domain.net", true),
("glob", "john@domain.net", false),
("glob", "example.org", false),
("list", "abc", true),
("list", "xyz", true),
("list", "zzz", false),
("regex", "user@domain.com", true),
("regex", "127.0.0.1", true),
("regex", "hello", false),
("suffix", "co.uk", true),
("suffix", "coco", false),
] {
assert_eq!(
lookups
.get(&format!("local/{lookup}"))
.unwrap()
.key_get::<String>(item.as_bytes().to_vec())
.await
.unwrap()
.is_some(),
expect,
"failed for {lookup}, item {item}"
);
}
}
*/
#[tokio::test]
async fn address_mappings() {
const MAPPINGS: &str = r#"

View File

@@ -4,11 +4,11 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use directory::{backend::internal::manage::ManageDirectory, Principal, QueryBy, Type};
use directory::{backend::internal::manage::ManageDirectory, QueryBy, Type};
use mail_send::Credentials;
use store::{LookupStore, Store};
use crate::directory::{map_account_ids, DirectoryTest};
use crate::directory::{map_account_ids, DirectoryTest, IntoTestPrincipal, TestPrincipal};
use super::DirectoryStore;
@@ -110,14 +110,19 @@ async fn sql_directory() {
)
.await
.unwrap()
.unwrap(),
Principal {
.unwrap()
.into_test(),
TestPrincipal {
id: base_store.get_account_id("john").await.unwrap().unwrap(),
name: "john".to_string(),
description: "John Doe".to_string().into(),
secrets: vec!["12345".to_string()],
typ: Type::Individual,
member_of: map_account_ids(base_store, vec!["sales"]).await,
member_of: map_account_ids(base_store, vec!["sales"])
.await
.into_iter()
.map(|v| v.to_string())
.collect(),
emails: vec![
"john@example.org".to_string(),
"jdoe@example.org".to_string(),
@@ -137,8 +142,9 @@ async fn sql_directory() {
)
.await
.unwrap()
.unwrap(),
Principal {
.unwrap()
.into_test(),
TestPrincipal {
id: base_store.get_account_id("bill").await.unwrap().unwrap(),
name: "bill".to_string(),
description: "Bill Foobar".to_string().into(),
@@ -169,14 +175,19 @@ async fn sql_directory() {
.query(QueryBy::Name("jane"), true)
.await
.unwrap()
.unwrap(),
Principal {
.unwrap()
.into_test(),
TestPrincipal {
id: base_store.get_account_id("jane").await.unwrap().unwrap(),
name: "jane".to_string(),
description: "Jane Doe".to_string().into(),
typ: Type::Individual,
secrets: vec!["abcde".to_string()],
member_of: map_account_ids(base_store, vec!["sales", "support"]).await,
member_of: map_account_ids(base_store, vec!["sales", "support"])
.await
.into_iter()
.map(|v| v.to_string())
.collect(),
emails: vec!["jane@example.org".to_string(),],
..Default::default()
}
@@ -188,8 +199,9 @@ async fn sql_directory() {
.query(QueryBy::Name("sales"), true)
.await
.unwrap()
.unwrap(),
Principal {
.unwrap()
.into_test(),
TestPrincipal {
id: base_store.get_account_id("sales").await.unwrap().unwrap(),
name: "sales".to_string(),
description: "Sales Team".to_string().into(),