systemprompt_security/authz/
scope_binding.rs1use systemprompt_identifiers::{ScopeDimension, UserId};
11use systemprompt_models::attribution::ScopeBinding;
12use thiserror::Error;
13
14use super::{AuthzError, SubjectProviderSet};
15
16#[derive(Debug, Error)]
18pub enum ScopeBindingError {
19 #[error("unknown scope dimension '{0}': no subject attribute provider registers it")]
20 UnknownDimension(ScopeDimension),
21
22 #[error("the key owner is not a member of {dimension} '{value}'")]
23 NotAMember {
24 dimension: ScopeDimension,
25 value: String,
26 },
27
28 #[error("subject attribute lookup failed: {0}")]
29 Lookup(#[from] AuthzError),
30}
31
32impl SubjectProviderSet {
33 pub async fn verify_scope_bindings(
34 &self,
35 owner: &UserId,
36 scopes: &[ScopeBinding],
37 ) -> Result<(), ScopeBindingError> {
38 for scope in scopes {
39 let Some(provider) = self.find(scope.dimension.as_str()) else {
40 return Err(ScopeBindingError::UnknownDimension(scope.dimension.clone()));
41 };
42 let held = provider.values_for(owner).await?;
43 if !held.iter().any(|value| value == &scope.value) {
44 return Err(ScopeBindingError::NotAMember {
45 dimension: scope.dimension.clone(),
46 value: scope.value.clone(),
47 });
48 }
49 }
50 Ok(())
51 }
52}