Files
Stalwart/tests/src/directory/oidc.rs

141 lines
5.2 KiB
Rust

/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
*
* SPDX-License-Identifier: LicenseRef-SEL
*
* This file is subject to the Stalwart Enterprise License Agreement (SEL) and
* is NOT open source software.
*
*/
use std::sync::Arc;
use base64::{Engine, engine::general_purpose};
use directory::QueryParams;
use http_proto::{JsonProblemResponse, JsonResponse, ToHttpResponse};
use hyper::{Method, StatusCode};
use mail_send::Credentials;
use serde_json::json;
use trc::{AuthEvent, EventType};
use crate::{
directory::DirectoryTest,
http_server::{HttpMessage, spawn_mock_http_server},
};
static TEST_TOKEN: &str = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ";
#[tokio::test]
async fn oidc_directory() {
// Obtain directory handle
let mut config = DirectoryTest::new("rocksdb".into()).await;
// Spawn mock OIDC server
let _tx = spawn_mock_http_server(Arc::new(|req: HttpMessage| {
let success_response = JsonResponse::new(json!({
"email": "john@example.org",
"preferred_username": "jdoe",
"name": "John Doe",
}))
.into_http_response();
match (req.method.clone(), req.uri.path().split('/').nth(1)) {
(Method::GET, Some("userinfo")) => match req.headers.get("authorization") {
Some(auth) if auth == &format!("Bearer {TEST_TOKEN}") => success_response,
Some(_) => JsonProblemResponse(StatusCode::UNAUTHORIZED).into_http_response(),
None => panic!("Missing Authorization header: {req:#?}"),
},
(Method::POST, Some("introspect-none")) => {
assert!(req.headers.get("authorization").is_none());
if req.get_url_encoded("token").as_deref() == Some(TEST_TOKEN) {
success_response
} else {
JsonProblemResponse(StatusCode::UNAUTHORIZED).into_http_response()
}
}
(Method::POST, Some("introspect-user-token")) => match req.headers.get("authorization")
{
Some(auth)
if auth == &format!("Bearer {TEST_TOKEN}")
&& req.get_url_encoded("token").as_deref() == Some(TEST_TOKEN) =>
{
success_response
}
Some(_) => JsonProblemResponse(StatusCode::UNAUTHORIZED).into_http_response(),
None => panic!("Missing Authorization header: {req:#?}"),
},
(Method::POST, Some("introspect-token")) => match req.headers.get("authorization") {
Some(auth)
if auth == "Bearer token_of_gratitude"
&& req.get_url_encoded("token").as_deref() == Some(TEST_TOKEN) =>
{
success_response
}
Some(_) => JsonProblemResponse(StatusCode::UNAUTHORIZED).into_http_response(),
None => panic!("Missing Authorization header: {req:#?}"),
},
(Method::POST, Some("introspect-basic")) => match req.headers.get("authorization") {
Some(auth)
if auth
== &format!(
"Basic {}",
general_purpose::STANDARD.encode("myuser:mypass".as_bytes())
)
&& req.get_url_encoded("token").as_deref() == Some(TEST_TOKEN) =>
{
success_response
}
Some(_) => JsonProblemResponse(StatusCode::UNAUTHORIZED).into_http_response(),
None => panic!("Missing Authorization header: {req:#?}"),
},
_ => panic!("Unexpected request: {:?}", req),
}
}))
.await;
for test in [
"oidc-userinfo",
"oidc-introspect-none",
"oidc-introspect-user-token",
"oidc-introspect-token",
"oidc-introspect-basic",
] {
println!("Running OIDC test {test:?}...");
let directory = config.directories.directories.remove(test).unwrap();
// Test an invalid token
let err = directory
.query(
QueryParams::credentials(&Credentials::OAuthBearer {
token: "invalid_or_expired_token".to_string(),
})
.with_return_member_of(false),
)
.await
.unwrap_err();
assert!(
err.matches(EventType::Auth(AuthEvent::Failed)),
"Unexpected error: {:?}",
err
);
// Test a valid token
let principal = directory
.query(
QueryParams::credentials(&Credentials::OAuthBearer {
token: TEST_TOKEN.to_string(),
})
.with_return_member_of(false),
)
.await
.unwrap()
.unwrap();
assert_eq!(principal.name(), "jdoe");
assert_eq!(
principal.emails.first().map(|s| s.as_str()),
Some("john@example.org")
);
assert_eq!(principal.description(), Some("John Doe"));
}
}