Skip to main content

typesec_odrl/
model.rs

1//! Serde data model for ODRL YAML policies.
2
3use std::fmt;
4
5use serde::{Deserialize, Deserializer, Serialize, Serializer};
6
7/// Root document: a collection of ODRL policies.
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct OdrlDocument {
10    /// The policies in this document.
11    pub policies: Vec<OdrlPolicy>,
12}
13
14impl OdrlDocument {
15    /// Parse from YAML.
16    pub fn from_yaml(yaml: &str) -> Result<Self, serde_yaml::Error> {
17        serde_yaml::from_str(yaml)
18    }
19}
20
21/// An ODRL Policy.
22///
23/// A policy bundles related rules under a unique identifier.
24#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct OdrlPolicy {
26    /// Unique identifier (e.g., `"policy:ai-agent-001"`).
27    pub uid: String,
28    /// Policy type — `Set`, `Offer`, or `Agreement`.
29    #[serde(rename = "type")]
30    pub policy_type: String,
31    /// The rules in this policy.
32    pub rules: Vec<OdrlRule>,
33}
34
35/// An ODRL Rule (permission, prohibition, or duty).
36#[derive(Debug, Clone, Serialize, Deserialize)]
37pub struct OdrlRule {
38    /// Rule type: `"permission"`, `"prohibition"`, or `"duty"`.
39    #[serde(rename = "type")]
40    pub rule_type: OdrlRuleType,
41    /// The party granting permission (optional for prohibitions).
42    #[serde(default)]
43    pub assigner: Option<String>,
44    /// The party the rule applies to.
45    pub assignee: String,
46    /// The action this rule covers.
47    pub action: RuleAction,
48    /// The asset this rule applies to.
49    pub target: String,
50    /// Constraints that must hold for the rule to apply.
51    #[serde(default)]
52    pub constraints: Vec<OdrlConstraint>,
53}
54
55/// The type of an ODRL rule.
56#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
57#[serde(rename_all = "lowercase")]
58pub enum OdrlRuleType {
59    /// Grants the assignee the action on the target (if constraints hold).
60    Permission,
61    /// Denies the assignee the action on the target (if constraints hold).
62    Prohibition,
63    /// Obligates the assignee to perform the action.
64    Duty,
65}
66
67/// An ODRL action.
68///
69/// Maps to our `Permission::name()` strings plus the special `"use"` wildcard.
70#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
71#[serde(rename_all = "lowercase")]
72pub enum RuleAction {
73    /// Read access.
74    Read,
75    /// Write access.
76    Write,
77    /// Delete access.
78    Delete,
79    /// Execute access.
80    Execute,
81    /// Delegation.
82    Delegate,
83    /// Read internal data.
84    #[serde(rename = "read_internal")]
85    ReadInternal,
86    /// Read sensitive data.
87    #[serde(rename = "read_sensitive")]
88    ReadSensitive,
89    /// Write sensitive data.
90    #[serde(rename = "write_sensitive")]
91    WriteSensitive,
92    /// AI inference.
93    #[serde(rename = "ai:infer")]
94    AiInfer,
95    /// AI training.
96    #[serde(rename = "ai:train")]
97    AiTrain,
98    /// Data exfiltration.
99    #[serde(rename = "exfiltrate")]
100    Exfiltrate,
101    /// Wildcard — applies to all actions.
102    Use,
103}
104
105impl RuleAction {
106    /// Convert to the `Permission::name()` string.
107    pub fn as_permission_name(&self) -> &str {
108        match self {
109            RuleAction::Read => "read",
110            RuleAction::Write => "write",
111            RuleAction::Delete => "delete",
112            RuleAction::Execute => "execute",
113            RuleAction::Delegate => "delegate",
114            RuleAction::ReadInternal => "read_internal",
115            RuleAction::ReadSensitive => "read_sensitive",
116            RuleAction::WriteSensitive => "write_sensitive",
117            RuleAction::AiInfer => "ai:infer",
118            RuleAction::AiTrain => "ai:train",
119            RuleAction::Exfiltrate => "ai:exfiltrate",
120            RuleAction::Use => "*",
121        }
122    }
123}
124
125/// An ODRL constraint on a rule.
126#[derive(Debug, Clone, Serialize, Deserialize)]
127pub struct OdrlConstraint {
128    /// The left operand (e.g., `"purpose"`, `"dateTime"`, `"count"`).
129    #[serde(rename = "leftOperand")]
130    pub left_operand: ConstraintOperand,
131    /// The comparison operator.
132    pub operator: ConstraintOperator,
133    /// The right operand value (string representation).
134    #[serde(rename = "rightOperand")]
135    pub right_operand: String,
136}
137
138/// Typed ODRL constraint operands supported by TypeSec.
139#[derive(Debug, Clone, PartialEq, Eq)]
140pub enum ConstraintOperand {
141    /// The request purpose.
142    Purpose,
143    /// The request timestamp; accepts `dateTime` and legacy `date` in YAML.
144    DateTime,
145    /// A count value supplied in the custom request context.
146    Count,
147    /// An extension operand supplied through custom request context.
148    Custom(String),
149}
150
151impl ConstraintOperand {
152    /// Parse an ODRL operand name.
153    pub fn parse(name: impl Into<String>) -> Self {
154        let name = name.into();
155        match name.as_str() {
156            "purpose" => Self::Purpose,
157            "dateTime" | "date" => Self::DateTime,
158            "count" => Self::Count,
159            _ => Self::Custom(name),
160        }
161    }
162
163    /// Return the canonical ODRL operand name.
164    pub fn as_str(&self) -> &str {
165        match self {
166            Self::Purpose => "purpose",
167            Self::DateTime => "dateTime",
168            Self::Count => "count",
169            Self::Custom(name) => name,
170        }
171    }
172}
173
174impl fmt::Display for ConstraintOperand {
175    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176        f.write_str(self.as_str())
177    }
178}
179
180impl Serialize for ConstraintOperand {
181    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
182    where
183        S: Serializer,
184    {
185        serializer.serialize_str(self.as_str())
186    }
187}
188
189impl<'de> Deserialize<'de> for ConstraintOperand {
190    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
191    where
192        D: Deserializer<'de>,
193    {
194        String::deserialize(deserializer).map(Self::parse)
195    }
196}
197
198/// ODRL constraint operators.
199#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
200#[serde(rename_all = "camelCase")]
201pub enum ConstraintOperator {
202    /// Equal.
203    Eq,
204    /// Not equal.
205    Neq,
206    /// Less than.
207    Lt,
208    /// Less than or equal.
209    Lteq,
210    /// Greater than.
211    Gt,
212    /// Greater than or equal.
213    Gteq,
214    /// Is in a comma-separated list.
215    #[serde(rename = "isPartOf")]
216    IsPartOf,
217}