use super::{AuthorizedIdentity, DenyReason};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AuthzDecision {
Allow {
identity: AuthorizedIdentity,
},
Deny {
reason: DenyReason,
detail: Option<String>,
},
}
impl AuthzDecision {
#[must_use]
pub const fn allow(identity: AuthorizedIdentity) -> Self {
Self::Allow { identity }
}
#[must_use]
pub const fn allow_anonymous() -> Self {
Self::Allow {
identity: AuthorizedIdentity::new(),
}
}
#[must_use]
pub const fn deny(reason: DenyReason) -> Self {
Self::Deny {
reason,
detail: None,
}
}
#[must_use]
pub fn deny_with_detail(reason: DenyReason, detail: impl Into<String>) -> Self {
Self::Deny {
reason,
detail: Some(detail.into()),
}
}
#[must_use]
pub const fn is_allowed(&self) -> bool {
matches!(self, Self::Allow { .. })
}
#[must_use]
pub const fn identity(&self) -> Option<&AuthorizedIdentity> {
match self {
Self::Allow { identity } => Some(identity),
Self::Deny { .. } => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn decision_constructors_and_predicate() {
let identity = AuthorizedIdentity::new()
.with_subject("system:serviceaccount:default:my-sa")
.with_audience("https://my-service.example");
let allowed = AuthzDecision::allow(identity.clone());
assert!(allowed.is_allowed());
assert_eq!(allowed.identity(), Some(&identity));
assert_eq!(
allowed.identity().and_then(|i| i.subject()),
Some("system:serviceaccount:default:my-sa")
);
assert!(AuthzDecision::allow_anonymous().is_allowed());
assert_eq!(
AuthzDecision::allow_anonymous().identity(),
Some(&AuthorizedIdentity::new())
);
assert!(!AuthzDecision::deny(DenyReason::MissingCredential).is_allowed());
assert!(
!AuthzDecision::deny_with_detail(DenyReason::NotPermitted, "rbac_failed").is_allowed()
);
assert_eq!(
AuthzDecision::deny(DenyReason::InvalidCredential).identity(),
None
);
assert_eq!(
AuthzDecision::deny_with_detail(DenyReason::NotPermitted, "nope"),
AuthzDecision::Deny {
reason: DenyReason::NotPermitted,
detail: Some("nope".to_owned())
}
);
assert_eq!(
AuthzDecision::deny(DenyReason::MissingCredential),
AuthzDecision::Deny {
reason: DenyReason::MissingCredential,
detail: None
}
);
}
}