#[allow(clippy::doc_markdown, clippy::too_many_arguments)]
pub mod action {
include!(concat!(env!("OUT_DIR"), "/action/mod.rs"));
}
#[allow(clippy::doc_markdown, clippy::too_many_arguments)]
pub mod policy {
include!(concat!(env!("OUT_DIR"), "/policy/mod.rs"));
}
#[allow(clippy::doc_markdown, clippy::too_many_arguments)]
pub mod verdict {
include!(concat!(env!("OUT_DIR"), "/verdict/mod.rs"));
}
#[allow(clippy::doc_markdown, clippy::too_many_arguments)]
pub mod audit {
include!(concat!(env!("OUT_DIR"), "/audit/mod.rs"));
}
#[allow(clippy::doc_markdown, clippy::too_many_arguments)]
pub mod control {
include!(concat!(env!("OUT_DIR"), "/control/mod.rs"));
}
#[allow(clippy::doc_markdown, clippy::too_many_arguments)]
pub mod provision {
include!(concat!(env!("OUT_DIR"), "/provision/mod.rs"));
}
pub fn empty_fields() -> serde_json::Value {
serde_json::Value::Object(serde_json::Map::new())
}
impl action::Action {
pub fn of(target: impl Into<String>, verb: action::Verb, resource: action::Resource) -> Self {
Self {
target: target.into(),
verb,
resource,
fields: empty_fields(),
}
}
#[must_use]
pub fn with_fields(mut self, fields: serde_json::Value) -> Self {
self.fields = fields;
self
}
}
impl action::Verb {
pub fn crud(kind: action::CrudKind) -> Self {
action::Verb::Crud(action::CrudVerb { kind })
}
pub fn action(id: impl Into<String>) -> Self {
action::Verb::Action(action::NamedVerb { id: id.into() })
}
pub fn parse(s: &str) -> Self {
match s.to_ascii_lowercase().as_str() {
"read" => Self::crud(action::CrudKind::Read),
"create" => Self::crud(action::CrudKind::Create),
"update" => Self::crud(action::CrudKind::Update),
"delete" => Self::crud(action::CrudKind::Delete),
_ => Self::action(s),
}
}
}
impl action::Resource {
pub fn of(path: impl Into<String>, kind: impl Into<String>) -> Self {
Self {
path: path.into(),
kind: kind.into(),
}
}
}
impl verdict::Verdict {
pub fn is_allow(&self) -> bool {
matches!(self, verdict::Verdict::Allow(_))
}
pub fn allow(obligations: Vec<verdict::Obligation>) -> Self {
verdict::Verdict::Allow(verdict::AllowVerdict { obligations })
}
pub fn inject(id: impl Into<String>) -> verdict::Obligation {
verdict::Obligation::InjectCredential(verdict::InjectCredentialObligation {
credential: verdict::CredentialRef { id: id.into() },
})
}
pub fn passthrough() -> verdict::Obligation {
verdict::Obligation::Passthrough(verdict::PassthroughObligation {})
}
pub fn deny(reason: verdict::DenyReason) -> Self {
verdict::Verdict::Deny(verdict::DenyVerdict { reason })
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
mod tests {
use super::action::{Action, CrudKind, Resource, Verb};
use super::verdict::{DenyReason, Verdict};
#[test]
fn action_round_trips_through_json() {
let action = Action::of(
"github",
Verb::crud(CrudKind::Create),
Resource::of("repos/octocat/hello/pulls", "pull_request"),
)
.with_fields(serde_json::json!({ "base": "main" }));
let json = serde_json::to_string(&action).unwrap();
let back: Action = serde_json::from_str(&json).unwrap();
assert_eq!(action, back);
}
#[test]
fn verb_parse_shorthand_maps_crud_words_and_named_actions() {
assert_eq!(Verb::parse("read"), Verb::crud(CrudKind::Read));
assert_eq!(Verb::parse("DELETE"), Verb::crud(CrudKind::Delete));
assert_eq!(
Verb::parse("ec2:DescribeInstances"),
Verb::action("ec2:DescribeInstances")
);
}
#[test]
fn verb_union_supports_crud_and_named_action() {
let read = Verb::crud(CrudKind::Read);
let terminate = Verb::action("ec2:TerminateInstances");
assert_ne!(read, terminate);
let json = serde_json::to_string(&terminate).unwrap();
let back: Verb = serde_json::from_str(&json).unwrap();
assert_eq!(terminate, back);
}
#[test]
fn verdict_helpers_build_expected_variants() {
let allow = Verdict::allow(vec![Verdict::inject("gh")]);
assert!(allow.is_allow());
let passthrough = Verdict::allow(vec![Verdict::passthrough()]);
assert!(passthrough.is_allow());
let deny = Verdict::deny(DenyReason::NotAllowed);
assert!(!deny.is_allow());
}
}