use std::collections::HashMap;
use fraiseql_core::security::{ENRICHED_NAMESPACE_PREFIX, SecurityContext};
use super::{failure::IdentityResolution, resolver::IdentityResolver};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EnrichmentOutcome {
Proceed,
Denied,
Unavailable,
}
pub async fn enrich_security_context(
resolver: &IdentityResolver,
ctx: &mut SecurityContext,
) -> EnrichmentOutcome {
let sub = ctx.user_id.0.clone();
let claims = claims_for_binding(ctx);
match resolver.resolve(&sub, &claims).await {
IdentityResolution::Resolved(fields) => {
for (field, value) in fields {
ctx.attributes.insert(format!("{ENRICHED_NAMESPACE_PREFIX}{field}"), value);
}
EnrichmentOutcome::Proceed
},
IdentityResolution::Denied(_) => EnrichmentOutcome::Denied,
IdentityResolution::Unavailable(_) => EnrichmentOutcome::Unavailable,
}
}
fn claims_for_binding(ctx: &SecurityContext) -> HashMap<String, serde_json::Value> {
let mut claims = ctx.attributes.clone();
claims
.entry("sub".to_owned())
.or_insert_with(|| serde_json::Value::String(ctx.user_id.0.clone()));
if let Some(tenant) = &ctx.tenant_id {
let value = serde_json::Value::String(tenant.0.clone());
claims.entry("tenant_id".to_owned()).or_insert_with(|| value.clone());
claims.entry("org_id".to_owned()).or_insert(value);
}
if let Some(email) = &ctx.email {
claims
.entry("email".to_owned())
.or_insert_with(|| serde_json::Value::String(email.clone()));
}
if let Some(name) = &ctx.display_name {
let value = serde_json::Value::String(name.clone());
claims.entry("name".to_owned()).or_insert_with(|| value.clone());
claims.entry("display_name".to_owned()).or_insert(value);
}
if let Some(iss) = &ctx.issuer {
claims
.entry("iss".to_owned())
.or_insert_with(|| serde_json::Value::String(iss.clone()));
}
claims
}