Allow local access tokens to be used with OIDC backends (closes #1311 closes stalwartlabs/webadmin#52)

This commit is contained in:
mdecimus
2025-07-25 21:06:23 +02:00
parent 02f6a114e0
commit 654f296d45
37 changed files with 369 additions and 278 deletions

View File

@@ -7,7 +7,7 @@
use std::sync::Arc;
use common::listener::limiter::{ConcurrencyLimiter, InFlight};
use directory::QueryBy;
use directory::QueryParams;
use mail_parser::decoders::base64::base64_decode;
use mail_send::Credentials;
use tokio::{
@@ -61,7 +61,9 @@ async fn imap_directory() {
assert_eq!(
&LookupResult::from(
handle
.query(QueryBy::Credentials(item.as_credentials()), true)
.query(
QueryParams::credentials(item.as_credentials()).with_return_member_of(true)
)
.await
.unwrap()
.is_some()
@@ -81,7 +83,10 @@ async fn imap_directory() {
tokio::spawn(async move {
LookupResult::from(
handle
.query(QueryBy::Credentials(item.as_credentials()), true)
.query(
QueryParams::credentials(item.as_credentials())
.with_return_member_of(true),
)
.await
.unwrap()
.is_some(),

View File

@@ -6,7 +6,7 @@
use ahash::AHashSet;
use directory::{
Permission, QueryBy, Type,
Permission, QueryBy, QueryParams, Type,
backend::{
RcptType,
internal::{
@@ -178,8 +178,8 @@ async fn internal_directory() {
assert_eq!(
store
.query(
QueryBy::Credentials(&Credentials::new("jane".into(), "my_secret".into())),
true
QueryParams::credentials(&Credentials::new("jane".into(), "my_secret".into()))
.with_return_member_of(true)
)
.await
.unwrap()
@@ -197,8 +197,11 @@ async fn internal_directory() {
assert_eq!(
store
.query(
QueryBy::Credentials(&Credentials::new("jane".into(), "wrong_password".into())),
true
QueryParams::credentials(&Credentials::new(
"jane".into(),
"wrong_password".into()
))
.with_return_member_of(true)
)
.await
.unwrap(),
@@ -275,7 +278,7 @@ async fn internal_directory() {
assert_eq!(
store
.query(QueryBy::Name("list"), true)
.query(QueryParams::name("list").with_return_member_of(true))
.await
.unwrap()
.unwrap()
@@ -353,7 +356,7 @@ async fn internal_directory() {
.is_ok()
);
let principal = store
.query(QueryBy::Name("john"), true)
.query(QueryParams::name("john").with_return_member_of(true))
.await
.unwrap()
.unwrap();
@@ -398,7 +401,7 @@ async fn internal_directory() {
.is_ok()
);
let principal = store
.query(QueryBy::Name("john"), true)
.query(QueryParams::name("john").with_return_member_of(true))
.await
.unwrap()
.unwrap();
@@ -448,7 +451,7 @@ async fn internal_directory() {
);
let principal = store
.query(QueryBy::Name("john.doe"), true)
.query(QueryParams::name("john.doe").with_return_member_of(true))
.await
.unwrap()
.unwrap();
@@ -791,7 +794,11 @@ impl TestInternalDirectory for Store {
) -> 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() {
if let Some(principal) = self
.query(QueryParams::name(login).with_return_member_of(false))
.await
.unwrap()
{
self.update_principal(UpdatePrincipal::by_id(principal.id()).with_updates(vec![
PrincipalUpdate::set(
PrincipalField::Secrets,
@@ -841,7 +848,11 @@ impl TestInternalDirectory for Store {
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() {
if let Some(principal) = self
.query(QueryParams::name(login).with_return_member_of(false))
.await
.unwrap()
{
principal.id()
} else {
self.create_principal(
@@ -866,7 +877,11 @@ impl TestInternalDirectory for Store {
}
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() {
if let Some(principal) = self
.query(QueryParams::name(login).with_return_member_of(false))
.await
.unwrap()
{
principal.id()
} else {
self.create_test_domains(&[login]).await;
@@ -958,7 +973,7 @@ impl TestInternalDirectory for Store {
for domain in domains {
let domain = domain.rsplit_once('@').map_or(*domain, |(_, d)| d);
if self
.query(QueryBy::Name(domain), false)
.query(QueryParams::name(domain).with_return_member_of(false))
.await
.unwrap()
.is_none()

View File

@@ -7,7 +7,7 @@
use std::fmt::Debug;
use directory::{
QueryBy, ROLE_USER, Type,
QueryParams, ROLE_USER, Type,
backend::{RcptType, internal::manage::ManageDirectory},
};
use mail_send::Credentials;
@@ -51,11 +51,11 @@ async fn ldap_directory() {
assert_eq!(
handle
.query(
QueryBy::Credentials(&Credentials::Plain {
QueryParams::credentials(&Credentials::Plain {
username: "john".into(),
secret: "12345".into()
}),
true
})
.with_return_member_of(true)
)
.await
.unwrap()
@@ -82,11 +82,11 @@ async fn ldap_directory() {
assert_eq!(
handle
.query(
QueryBy::Credentials(&Credentials::Plain {
QueryParams::credentials(&Credentials::Plain {
username: "bill".into(),
secret: "password".into()
}),
true
})
.with_return_member_of(true)
)
.await
.unwrap()
@@ -111,11 +111,11 @@ async fn ldap_directory() {
assert!(
handle
.query(
QueryBy::Credentials(&Credentials::Plain {
QueryParams::credentials(&Credentials::Plain {
username: "bill".into(),
secret: "invalid".into()
}),
true
})
.with_return_member_of(true)
)
.await
.unwrap()
@@ -126,7 +126,7 @@ async fn ldap_directory() {
// Get user by name
assert_eq!(
handle
.query(QueryBy::Name("jane"), true)
.query(QueryParams::name("jane").with_return_member_of(true))
.await
.unwrap()
.unwrap()
@@ -153,7 +153,7 @@ async fn ldap_directory() {
// Get group by name
assert_eq!(
handle
.query(QueryBy::Name("sales"), true)
.query(QueryParams::name("sales").with_return_member_of(true))
.await
.unwrap()
.unwrap()

View File

@@ -11,7 +11,7 @@
use std::sync::Arc;
use base64::{Engine, engine::general_purpose};
use directory::QueryBy;
use directory::QueryParams;
use http_proto::{JsonProblemResponse, JsonResponse, ToHttpResponse};
use hyper::{Method, StatusCode};
use mail_send::Credentials;
@@ -106,10 +106,10 @@ async fn oidc_directory() {
// Test an invalid token
let err = directory
.query(
QueryBy::Credentials(&Credentials::OAuthBearer {
QueryParams::credentials(&Credentials::OAuthBearer {
token: "invalid_or_expired_token".to_string(),
}),
false,
})
.with_return_member_of(false),
)
.await
.unwrap_err();
@@ -122,10 +122,10 @@ async fn oidc_directory() {
// Test a valid token
let principal = directory
.query(
QueryBy::Credentials(&Credentials::OAuthBearer {
QueryParams::credentials(&Credentials::OAuthBearer {
token: TEST_TOKEN.to_string(),
}),
false,
})
.with_return_member_of(false),
)
.await
.unwrap()

View File

@@ -7,7 +7,7 @@
use super::dummy_tls_acceptor;
use crate::directory::{DirectoryTest, Item, LookupResult};
use common::listener::limiter::{ConcurrencyLimiter, InFlight};
use directory::{QueryBy, backend::RcptType};
use directory::{QueryParams, backend::RcptType};
use mail_parser::decoders::base64::base64_decode;
use mail_send::Credentials;
use std::sync::Arc;
@@ -76,7 +76,7 @@ async fn lmtp_directory() {
(core.rcpt(&handle, v, 0).await.unwrap() == RcptType::Mailbox).into()
}
Item::Authenticate(v) => handle
.query(QueryBy::Credentials(v), true)
.query(QueryParams::credentials(v).with_return_member_of(true))
.await
.unwrap()
.is_some()
@@ -122,7 +122,7 @@ async fn lmtp_directory() {
(core.rcpt(&handle, v, 0).await.unwrap() == RcptType::Mailbox).into()
}
Item::Authenticate(v) => handle
.query(QueryBy::Credentials(v), true)
.query(QueryParams::credentials(v).with_return_member_of(true))
.await
.unwrap()
.is_some()

View File

@@ -5,7 +5,7 @@
*/
use directory::{
QueryBy, ROLE_USER, Type,
QueryParams, ROLE_USER, Type,
backend::{RcptType, internal::manage::ManageDirectory},
};
use mail_send::Credentials;
@@ -113,11 +113,11 @@ async fn sql_directory() {
assert_eq!(
handle
.query(
QueryBy::Credentials(&Credentials::Plain {
QueryParams::credentials(&Credentials::Plain {
username: "john".into(),
secret: "12345".into()
}),
true
})
.with_return_member_of(true)
)
.await
.unwrap()
@@ -146,11 +146,11 @@ async fn sql_directory() {
assert_eq!(
handle
.query(
QueryBy::Credentials(&Credentials::Plain {
QueryParams::credentials(&Credentials::Plain {
username: "bill".into(),
secret: "password".into()
}),
true
})
.with_return_member_of(true)
)
.await
.unwrap()
@@ -173,11 +173,11 @@ async fn sql_directory() {
assert_eq!(
handle
.query(
QueryBy::Credentials(&Credentials::Plain {
QueryParams::credentials(&Credentials::Plain {
username: "admin".into(),
secret: "very_secret".into()
}),
true
})
.with_return_member_of(true)
)
.await
.unwrap()
@@ -196,11 +196,11 @@ async fn sql_directory() {
assert!(
handle
.query(
QueryBy::Credentials(&Credentials::Plain {
QueryParams::credentials(&Credentials::Plain {
username: "bill".into(),
secret: "invalid".into()
}),
true
})
.with_return_member_of(true)
)
.await
.unwrap()
@@ -210,7 +210,7 @@ async fn sql_directory() {
// Get user by name
assert_eq!(
handle
.query(QueryBy::Name("jane"), true)
.query(QueryParams::name("jane").with_return_member_of(true))
.await
.unwrap()
.unwrap()
@@ -235,7 +235,7 @@ async fn sql_directory() {
// Get group by name
assert_eq!(
handle
.query(QueryBy::Name("sales"), true)
.query(QueryParams::name("sales").with_return_member_of(true))
.await
.unwrap()
.unwrap()