use systemprompt_identifiers::{ScopeDimension, UserId};
use systemprompt_models::attribution::ScopeBinding;
use thiserror::Error;
use super::{AuthzError, SubjectProviderSet};
#[derive(Debug, Error)]
pub enum ScopeBindingError {
#[error("unknown scope dimension '{0}': no subject attribute provider registers it")]
UnknownDimension(ScopeDimension),
#[error("the key owner is not a member of {dimension} '{value}'")]
NotAMember {
dimension: ScopeDimension,
value: String,
},
#[error("subject attribute lookup failed: {0}")]
Lookup(#[from] AuthzError),
}
impl SubjectProviderSet {
pub async fn verify_scope_bindings(
&self,
owner: &UserId,
scopes: &[ScopeBinding],
) -> Result<(), ScopeBindingError> {
for scope in scopes {
let Some(provider) = self.find(scope.dimension.as_str()) else {
return Err(ScopeBindingError::UnknownDimension(scope.dimension.clone()));
};
let held = provider.values_for(owner).await?;
if !held.iter().any(|value| value == &scope.value) {
return Err(ScopeBindingError::NotAMember {
dimension: scope.dimension.clone(),
value: scope.value.clone(),
});
}
}
Ok(())
}
}