use runx_contracts::JsonValue;
use crate::policy::{
LocalAdmissionGrant, LocalScopeAdmissionOptions, ScopeAdmission, ScopeAdmissionStatus,
credential_grant::{credential_grant_requirement, find_matching_grant},
scope::unique_strings,
};
use super::util::{non_empty_option, non_empty_vec};
#[must_use]
pub fn build_local_scope_admission(
auth: Option<&JsonValue>,
grants: &[LocalAdmissionGrant],
options: &LocalScopeAdmissionOptions,
) -> ScopeAdmission {
let requirement = match credential_grant_requirement(auth) {
Ok(Some(requirement)) => requirement,
Ok(None) => {
return scope_admission_allow(
Vec::new(),
Vec::new(),
None,
"no connected auth requested",
);
}
Err(error) => {
return scope_admission_deny(
Vec::new(),
Vec::new(),
vec![error.message().to_owned()],
"invalid connected auth request",
);
}
};
let requested_scopes = unique_strings(&requirement.scopes);
if options.denied_before_grant_resolution.unwrap_or(false) {
return scope_admission_deny(
requested_scopes,
Vec::new(),
vec!["structural policy denied before connected auth grant resolution".to_owned()],
"structural policy denied before grant resolution",
);
}
match find_matching_grant(
&requirement,
grants,
options.connected_auth_checked_at.as_deref(),
options.wildcard_scopes_trusted,
) {
Some(grant) => scope_admission_allow(
requested_scopes,
unique_strings(&grant.scopes),
Some(grant.grant_id.clone()),
"matching active grant admitted",
),
None => scope_admission_deny(
requested_scopes,
Vec::new(),
vec![format!(
"connected auth grant required for provider '{}'",
requirement.provider
)],
"no matching active grant resolved",
),
}
}
fn scope_admission_allow(
requested_scopes: Vec<String>,
granted_scopes: Vec<String>,
grant_id: Option<String>,
summary: &str,
) -> ScopeAdmission {
ScopeAdmission {
status: ScopeAdmissionStatus::Allow,
requested_scopes: non_empty_vec(requested_scopes),
granted_scopes: non_empty_vec(granted_scopes),
grant_id: non_empty_option(grant_id),
reasons: None,
decision_summary: Some(summary.to_owned()),
}
}
fn scope_admission_deny(
requested_scopes: Vec<String>,
granted_scopes: Vec<String>,
reasons: Vec<String>,
summary: &str,
) -> ScopeAdmission {
ScopeAdmission {
status: ScopeAdmissionStatus::Deny,
requested_scopes: non_empty_vec(requested_scopes),
granted_scopes: non_empty_vec(granted_scopes),
grant_id: None,
reasons: Some(non_empty_vec(reasons)),
decision_summary: Some(summary.to_owned()),
}
}