use ahash::HashMapExt as _;
use std::{collections::HashMap, sync::Arc};
use crate::{
LogLevel,
common::{issuer_utils::IssClaim, policy_store::TrustedIssuer},
log::{BaseLogEntry, LogEntry, LogWriter, Logger},
};
#[derive(Debug, Clone)]
pub(crate) struct TrustedIssuerIndex {
url_index: ahash::HashMap<IssClaim, Arc<TrustedIssuer>>,
origin_index: ahash::HashMap<IssClaim, Arc<TrustedIssuer>>,
}
impl TrustedIssuerIndex {
pub(crate) fn new(issuers: &HashMap<String, TrustedIssuer>, logger: Option<&Logger>) -> Self {
let mut origin_index: ahash::HashMap<IssClaim, Arc<TrustedIssuer>> = ahash::HashMap::new();
let mut url_index: ahash::HashMap<IssClaim, Arc<TrustedIssuer>> = ahash::HashMap::new();
let mut sorted: Vec<(&String, &TrustedIssuer)> = issuers.iter().collect();
sorted.sort_by(|a, b| a.0.cmp(b.0));
for (_, iss) in sorted {
let origin = iss.iss_claim();
let full_url = IssClaim::new(iss.get_oidc_endpoint().as_str());
let issuer = Arc::new(iss.clone());
if let Some(existing) = origin_index.get(&origin) {
logger.log_any(
LogEntry::new(BaseLogEntry::new_system_opt_request_id(LogLevel::WARN, None))
.set_message(format!(
"Duplicate origin '{}': issuer '{}' will override existing issuer '{}' for origin-based lookups",
origin, iss.name, existing.name,
)),
);
}
origin_index.insert(origin, issuer.clone());
url_index.insert(full_url, issuer);
}
Self {
url_index,
origin_index,
}
}
pub(super) fn find(&self, iss: &IssClaim) -> Option<&Arc<TrustedIssuer>> {
self.origin_index
.get(iss)
.or_else(|| self.url_index.get(iss))
}
pub(super) fn values(&self) -> impl Iterator<Item = &TrustedIssuer> + '_ {
self.url_index.values().map(AsRef::as_ref)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::common::policy_store::TrustedIssuer;
use std::collections::HashMap;
use url::Url;
fn create_issuer_index() -> TrustedIssuerIndex {
let mut trusted_issuers = HashMap::new();
let issuer_one = TrustedIssuer::new(
"Issuer One".to_string(),
"Issuer One".to_string(),
Url::parse("https://issuer1.example.com/auth").unwrap(),
HashMap::new(),
);
trusted_issuers.insert("issuer_one".to_string(), issuer_one);
let jans = TrustedIssuer::new(
"Jans".to_string(),
"Janssen".to_string(),
Url::parse("https://account.gluu.org/.well-known/openid-configuration").unwrap(),
HashMap::new(),
);
trusted_issuers.insert("jans".to_string(), jans);
let microsoft_issuer = TrustedIssuer::new(
"Microsoft".to_string(),
"Microsoft Azure AD".to_string(),
Url::parse("https://login.microsoftonline.com/tenant").unwrap(),
HashMap::new(),
);
trusted_issuers.insert("microsoft".to_string(), microsoft_issuer);
let company_issuer = TrustedIssuer::new(
"Company".to_string(),
"Company Internal Auth".to_string(),
Url::parse("https://auth.company.internal:8443/oauth").unwrap(),
HashMap::new(),
);
trusted_issuers.insert("company".to_string(), company_issuer);
TrustedIssuerIndex::new(&trusted_issuers, None)
}
#[test]
fn test_empty_issuer_index() {
let issuer_index = TrustedIssuerIndex::new(&HashMap::new(), None);
assert!(
issuer_index
.find(&IssClaim::new("https://login.microsoftonline.com/tenant"))
.is_none(),
"expected no issuer to be found in empty index for IssClaim::new(\"https://login.microsoftonline.com/tenant\")"
);
}
#[test]
fn test_find_non_existing_issuer() {
let issuer_index = create_issuer_index();
assert!(
issuer_index
.find(&IssClaim::new("https://account.google.com"))
.is_none(),
"expected no issuer to be found for IssClaim::new(\"https://account.google.com\")"
);
}
#[test]
fn test_find_by_full_url() {
let issuer_index = create_issuer_index();
let issuer = issuer_index
.find(&IssClaim::new("https://login.microsoftonline.com/tenant"))
.expect("Should find issuer by full URL");
assert_eq!(issuer.name, "Microsoft");
assert_eq!(
issuer.get_oidc_endpoint().as_str(),
"https://login.microsoftonline.com/tenant"
);
}
#[test]
fn test_find_by_origin() {
let issuer_index = create_issuer_index();
let issuer = issuer_index
.find(&IssClaim::new("https://account.gluu.org"))
.expect("Should find issuer by origin");
assert_eq!(issuer.name, "Jans");
assert_eq!(
issuer.get_oidc_endpoint().as_str(),
"https://account.gluu.org/.well-known/openid-configuration"
);
}
#[test]
fn test_find_by_origin_with_trailing_slash() {
let issuer_index = create_issuer_index();
let issuer = issuer_index
.find(&IssClaim::new("https://account.gluu.org/"))
.expect("Should find issuer by origin with trailing slash");
assert_eq!(
issuer.name, "Jans",
"lookup-by-origin-with-trailing-slash should normalize the trailing slash and resolve to the 'Jans' issuer"
);
}
#[test]
fn test_find_by_origin_with_port() {
let issuer_index = create_issuer_index();
let issuer = issuer_index
.find(&IssClaim::new("https://auth.company.internal:8443"))
.expect("Should find issuer by origin with port");
assert_eq!(issuer.name, "Company");
assert_eq!(
issuer.get_oidc_endpoint().as_str(),
"https://auth.company.internal:8443/oauth"
);
}
}