Skip to main content

typesec_rbac/graph_policy/
rule.rs

1//! The graph-policy rule and condition AST.
2
3use std::collections::BTreeMap;
4
5use grust::prelude::{Graph, Value};
6use serde::{Deserialize, Deserializer, Serialize};
7
8pub fn deserialize_graph<'de, D>(deserializer: D) -> Result<Graph, D::Error>
9where
10    D: Deserializer<'de>,
11{
12    let value = serde_yaml::Value::deserialize(deserializer)?;
13    let yaml = serde_yaml::to_string(&value).map_err(serde::de::Error::custom)?;
14    Graph::from_yaml(&yaml).map_err(serde::de::Error::custom)
15}
16
17#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct GraphRule {
19    #[serde(default = "allow_effect")]
20    pub effect: RuleEffect,
21    #[serde(default)]
22    pub subject: Option<String>,
23    #[serde(default)]
24    pub subject_has_role: Option<String>,
25    pub action: String,
26    pub resource: String,
27    #[serde(default, rename = "where")]
28    pub conditions: GraphConditions,
29}
30
31#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
32#[serde(rename_all = "snake_case")]
33pub enum RuleEffect {
34    Allow,
35    Deny,
36}
37
38fn allow_effect() -> RuleEffect {
39    RuleEffect::Allow
40}
41
42#[derive(Debug, Clone, Default, Serialize, Deserialize)]
43pub struct GraphConditions {
44    #[serde(default)]
45    pub target: Option<TargetCondition>,
46    #[serde(default)]
47    pub relationship: Option<RelationshipCondition>,
48    #[serde(default)]
49    pub path_exists: Option<PathCondition>,
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize)]
53pub struct TargetCondition {
54    pub resource_prefix: String,
55    #[serde(default)]
56    pub label: Option<String>,
57    #[serde(default)]
58    pub property_equals: BTreeMap<String, Scalar>,
59    #[serde(default)]
60    pub property_not_equals: BTreeMap<String, Scalar>,
61}
62
63#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct RelationshipCondition {
65    pub resource_prefix: String,
66    pub edge_label: String,
67    #[serde(default)]
68    pub from_label: Option<String>,
69    #[serde(default)]
70    pub to_label: Option<String>,
71    #[serde(default)]
72    pub no_cycle: bool,
73    #[serde(default)]
74    pub from_property_equals: BTreeMap<String, Scalar>,
75    #[serde(default)]
76    pub to_property_equals: BTreeMap<String, Scalar>,
77    #[serde(default)]
78    pub from_property_not_equals: BTreeMap<String, Scalar>,
79    #[serde(default)]
80    pub to_property_not_equals: BTreeMap<String, Scalar>,
81}
82
83#[derive(Debug, Clone, Serialize, Deserialize)]
84pub struct PathCondition {
85    pub from: String,
86    pub to: String,
87    pub edge: String,
88    #[serde(default)]
89    pub direction: PathDirection,
90}
91
92#[derive(Debug, Clone, Default, Serialize, Deserialize)]
93#[serde(rename_all = "snake_case")]
94pub enum PathDirection {
95    #[default]
96    Out,
97    In,
98    Both,
99}
100
101#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
102#[serde(untagged)]
103pub enum Scalar {
104    Bool(bool),
105    Int(i64),
106    Float(f64),
107    String(String),
108}
109
110impl Scalar {
111    pub(crate) fn as_value(&self) -> Value {
112        match self {
113            Self::Bool(value) => Value::from(*value),
114            Self::Int(value) => Value::from(*value),
115            Self::Float(value) => Value::from(*value),
116            Self::String(value) => Value::from(value),
117        }
118    }
119}