#![cfg(feature = "server-test")]
use std::sync::Arc;
use foundation_auth::server::handlers::provider_admin::{
self, CreateProviderRequest, ProviderEntry, SetSecretRequest,
};
use foundation_auth::server::models::provider::{ProviderType, UpstreamProvider};
use foundation_auth::server::services::provider_service::{ProviderCrypto, ProviderService};
use foundation_core::valtron::valtron_test;
use foundation_db::{QueryStore, StorageBackend, StorageProvider};
use tempfile::TempDir;
fn make_service() -> (ProviderService<StorageProvider>, TempDir) {
let dir = tempfile::tempdir().expect("tempdir");
let db_path = dir.path().join("admin_test.db");
let store = Arc::new(
StorageProvider::new(StorageBackend::Turso {
url: db_path.to_str().unwrap().to_string(),
})
.expect("init turso"),
);
let crypto = ProviderCrypto::from_passphrase("test-key", "default");
(ProviderService::new(store, crypto), dir)
}
#[valtron_test]
fn extended_discovery_lists_active_providers() {
let (svc, _tmp) = make_service();
let google = sample_google();
svc.create(google.clone()).expect("create google");
let mut github = sample_github();
github.is_active = false;
svc.create(github).expect("create github");
let providers = svc.find_active().expect("find_active");
assert_eq!(providers.len(), 1, "only google is active");
use foundation_auth::server::config::IdpConfig;
let config = IdpConfig::new("https://auth.example.com".into());
let resp = provider_admin::extended_discovery(&config, "/idp", &providers).expect("discovery");
assert_eq!(resp.status, 200);
let doc: serde_json::Value = resp.body;
assert_eq!(doc["providers_supported"].as_array().unwrap().len(), 1);
assert_eq!(doc["providers_supported"][0]["id"], "google");
assert!(doc["social_login_endpoint"].as_str().unwrap().contains("/authorize"));
}
#[valtron_test]
fn list_all_includes_inactive() {
let (svc, _tmp) = make_service();
svc.create(sample_google()).expect("create google");
svc.create(sample_github()).expect("create github");
let resp = provider_admin::list_all(&svc).expect("list_all");
let body: serde_json::Value = resp.body;
let providers = body["providers"].as_array().unwrap();
assert_eq!(providers.len(), 2);
}
#[valtron_test]
fn get_one_returns_provider() {
let (svc, _tmp) = make_service();
svc.create(sample_google()).expect("create");
let resp = provider_admin::get_one(&svc, "google").expect("get");
let entry: ProviderEntry =
serde_json::from_value(resp.body).expect("deserialize");
assert_eq!(entry.id, "google");
assert_eq!(entry.name, "Google");
assert_eq!(entry.provider_type, "oidc");
}
#[valtron_test]
fn get_one_missing_returns_error() {
let (svc, _tmp) = make_service();
let err = provider_admin::get_one(&svc, "nope").unwrap_err();
assert!(err.to_string().contains("not found"));
}
#[valtron_test]
fn create_provider_with_discovery_url() {
let (svc, _tmp) = make_service();
let req = CreateProviderRequest {
id: "google".into(),
name: "Google".into(),
provider_type: "oidc".into(),
client_id: "test-client".into(),
discovery_url: Some("https://accounts.google.com/.well-known/openid-configuration".into()),
authorization_url: None,
token_url: None,
userinfo_url: None,
scopes: vec!["openid".into(), "email".into()],
client_secret: Some("secret-123".into()),
};
let resp = provider_admin::create(&svc, req).expect("create");
let entry: ProviderEntry =
serde_json::from_value(resp.body).expect("deserialize");
assert_eq!(entry.id, "google");
assert!(entry.is_active);
}
#[valtron_test]
fn create_rejects_invalid_provider_type() {
let (svc, _tmp) = make_service();
let mut req = create_req("bad");
req.provider_type = "saml".into();
let err = provider_admin::create(&svc, req).unwrap_err();
assert!(err.to_string().contains("unknown provider_type"));
}
#[valtron_test]
fn update_changes_name_and_scopes() {
let (svc, _tmp) = make_service();
svc.create(sample_google()).expect("create");
let req = CreateProviderRequest {
id: "google".into(),
name: "Google SSO Updated".into(),
provider_type: "oidc".into(),
client_id: "test-client".into(),
discovery_url: None,
authorization_url: Some("https://example.com/auth".into()),
token_url: Some("https://example.com/token".into()),
userinfo_url: None,
scopes: vec!["openid".into()],
client_secret: None,
};
let resp = provider_admin::update(&svc, "google", req).expect("update");
let entry: ProviderEntry =
serde_json::from_value(resp.body).expect("deserialize");
assert_eq!(entry.name, "Google SSO Updated");
assert_eq!(entry.scopes, vec!["openid"]);
}
#[valtron_test]
fn delete_deactivates_provider() {
let (svc, _tmp) = make_service();
svc.create(sample_google()).expect("create");
let resp = provider_admin::delete(&svc, "google").expect("delete");
let body: serde_json::Value = resp.body;
assert_eq!(body["deleted"], "google");
assert!(svc.find_by_id("google").unwrap().is_none());
}
#[valtron_test]
fn set_secret_encrypts_and_stores() {
let (svc, _tmp) = make_service();
svc.create(sample_google()).expect("create");
let req = SetSecretRequest {
secret: "my-secret".into(),
};
let resp = provider_admin::set_secret(&svc, "google", req).expect("set_secret");
assert_eq!(resp.body["ok"], true);
let stored = svc.get_secret("google").expect("get_secret");
assert_eq!(stored, "my-secret");
}
#[valtron_test]
fn set_secret_on_missing_provider_errors() {
let (svc, _tmp) = make_service();
let req = SetSecretRequest {
secret: "secret".into(),
};
let err = provider_admin::set_secret(&svc, "nope", req).unwrap_err();
assert!(err.to_string().contains("not found") || err.to_string().contains("Not"));
}
fn sample_google() -> UpstreamProvider {
UpstreamProvider {
id: "google".into(),
name: "Google".into(),
provider_type: ProviderType::Oidc,
client_id: "google-client-id".into(),
client_secret_ciphertext: None,
encryption_key_id: "default".into(),
authorization_url: None,
token_url: None,
userinfo_url: None,
discovery_url: Some(
"https://accounts.google.com/.well-known/openid-configuration".into(),
),
scopes: vec!["openid".into(), "email".into(), "profile".into()],
is_active: true,
mapping_config: Default::default(),
created_at: 0,
updated_at: 0,
}
}
fn sample_github() -> UpstreamProvider {
UpstreamProvider {
id: "github".into(),
name: "GitHub".into(),
provider_type: ProviderType::Oauth2,
client_id: "github-client-id".into(),
client_secret_ciphertext: None,
encryption_key_id: "default".into(),
authorization_url: Some("https://github.com/login/oauth/authorize".into()),
token_url: Some("https://github.com/login/oauth/access_token".into()),
userinfo_url: Some("https://api.github.com/user".into()),
discovery_url: None,
scopes: vec!["user:email".into()],
is_active: false,
mapping_config: Default::default(),
created_at: 0,
updated_at: 0,
}
}
fn create_req(id: &str) -> CreateProviderRequest {
CreateProviderRequest {
id: id.into(),
name: "Test Provider".into(),
provider_type: "oidc".into(),
client_id: "test-client".into(),
discovery_url: Some("https://example.com/.well-known/openid-configuration".into()),
authorization_url: None,
token_url: None,
userinfo_url: None,
scopes: vec![],
client_secret: None,
}
}