use async_trait::async_trait;
use crate::error::Result;
use crate::federation::FederatedProfile;
#[derive(Debug, Clone)]
pub struct SamlAssertion {
pub issuer: String,
pub name_id: String,
pub attributes: std::collections::HashMap<String, Vec<String>>,
pub email_verified: bool,
}
impl SamlAssertion {
fn attr(&self, key: &str) -> Option<&str> {
self.attributes
.get(key)
.and_then(|v| v.first())
.map(String::as_str)
}
pub fn to_profile(&self, provider: impl Into<String>) -> FederatedProfile {
let mut attributes = serde_json::Map::new();
for (k, v) in &self.attributes {
attributes.insert(k.clone(), serde_json::json!(v));
}
FederatedProfile {
provider: provider.into(),
provider_id: self.name_id.clone(),
email: self
.attr("email")
.or_else(|| self.attr("urn:oid:0.9.2342.19200300.100.1.3"))
.map(str::to_owned),
email_verified: self.email_verified,
name: self.attr("displayName").map(str::to_owned),
attributes,
}
}
}
#[async_trait]
pub trait SamlProvider: Send + Sync + 'static {
fn id(&self) -> &str;
fn authn_request_url(&self, relay_state: &str) -> Result<String>;
async fn verify_response(&self, saml_response_b64: &str) -> Result<SamlAssertion>;
}