use tonic::{Status, metadata::MetadataMap};
pub fn validate_and_resolve_user_attribution_grpc(
metadata: &MetadataMap,
) -> Result<(String, String, Option<String>), Box<Status>> {
let auth_subject = metadata.get("x-auth-subject").and_then(|v| v.to_str().ok());
let auth_email = metadata.get("x-auth-email").and_then(|v| v.to_str().ok());
let allow_delegation = metadata
.get("x-allow-delegation")
.and_then(|v| v.to_str().ok())
.and_then(|s| s.parse::<bool>().ok())
.unwrap_or(false);
let claimed_user_id = metadata.get("x-user-id").and_then(|v| v.to_str().ok());
let claimed_user_email = metadata.get("x-user-email").and_then(|v| v.to_str().ok());
let Some(authenticated_subject) = auth_subject else {
return Ok((
claimed_user_id.unwrap_or("unknown").to_string(),
claimed_user_email.unwrap_or("unknown").to_string(),
None,
));
};
if allow_delegation {
let user_id = claimed_user_id.unwrap_or(authenticated_subject).to_string();
let user_email = claimed_user_email
.or(auth_email)
.unwrap_or("service-account")
.to_string();
let service_account = if claimed_user_id.is_some() || claimed_user_email.is_some() {
Some(authenticated_subject.to_string())
} else {
None
};
Ok((user_id, user_email, service_account))
} else {
if let Some(claimed_id) = claimed_user_id
&& claimed_id != authenticated_subject
{
return Err(Box::new(Status::permission_denied(format!(
"User impersonation not allowed: x-user-id '{}' does not match authenticated subject '{}'",
claimed_id, authenticated_subject
))));
}
if let (Some(claimed_email), Some(authenticated_email)) = (claimed_user_email, auth_email)
&& claimed_email != authenticated_email
{
return Err(Box::new(Status::permission_denied(format!(
"User impersonation not allowed: x-user-email '{}' does not match authenticated email '{}'",
claimed_email, authenticated_email
))));
}
let user_id = authenticated_subject.to_string();
let user_email = auth_email.unwrap_or("unknown").to_string();
Ok((user_id, user_email, None))
}
}