Skip to main content

solid_pod_rs/wac/
document.rs

1//! ACL document AST — JSON-LD deserialisation shape.
2//!
3//! The same struct shape is produced by the Turtle parser in
4//! `wac::parser` so downstream consumers work with a single canonical
5//! representation regardless of wire format.
6
7use serde::{Deserialize, Serialize};
8
9use crate::wac::conditions::Condition;
10
11/// Parsed ACL document containing zero or more authorization rules.
12#[derive(Debug, Clone, Deserialize, Serialize)]
13pub struct AclDocument {
14    /// JSON-LD `@context` value, if the document was parsed from JSON-LD.
15    #[serde(rename = "@context", skip_serializing_if = "Option::is_none")]
16    pub context: Option<serde_json::Value>,
17
18    /// The list of authorization rules in this document.
19    #[serde(rename = "@graph", skip_serializing_if = "Option::is_none")]
20    pub graph: Option<Vec<AclAuthorization>>,
21}
22
23/// A single WAC authorization rule describing who may access what, and how.
24#[derive(Debug, Clone, Deserialize, Serialize)]
25pub struct AclAuthorization {
26    /// Node identifier for this authorization (e.g. `#owner`).
27    #[serde(rename = "@id", skip_serializing_if = "Option::is_none")]
28    pub id: Option<String>,
29
30    /// RDF type, typically `acl:Authorization`.
31    #[serde(rename = "@type", skip_serializing_if = "Option::is_none")]
32    pub r#type: Option<String>,
33
34    /// WebID(s) of the agent(s) this rule grants access to.
35    #[serde(rename = "acl:agent", skip_serializing_if = "Option::is_none")]
36    pub agent: Option<IdOrIds>,
37
38    /// Agent class (`foaf:Agent` for public, `acl:AuthenticatedAgent` for logged-in).
39    #[serde(rename = "acl:agentClass", skip_serializing_if = "Option::is_none")]
40    pub agent_class: Option<IdOrIds>,
41
42    /// IRI(s) of `vcard:Group` documents whose members are granted access.
43    #[serde(rename = "acl:agentGroup", skip_serializing_if = "Option::is_none")]
44    pub agent_group: Option<IdOrIds>,
45
46    /// Permitted request origin(s) per WAC section 4.3.
47    #[serde(rename = "acl:origin", skip_serializing_if = "Option::is_none")]
48    pub origin: Option<IdOrIds>,
49
50    /// Resource path(s) this rule applies to (exact match + direct children).
51    #[serde(rename = "acl:accessTo", skip_serializing_if = "Option::is_none")]
52    pub access_to: Option<IdOrIds>,
53
54    /// Container path(s) whose descendants inherit this rule recursively.
55    #[serde(rename = "acl:default", skip_serializing_if = "Option::is_none")]
56    pub default: Option<IdOrIds>,
57
58    /// Access mode(s) granted: `acl:Read`, `acl:Write`, `acl:Append`, `acl:Control`.
59    #[serde(rename = "acl:mode", skip_serializing_if = "Option::is_none")]
60    pub mode: Option<IdOrIds>,
61
62    /// WAC 2.0 conjunctive conditions. Every listed condition must be
63    /// `Satisfied` for this authorisation to grant access. An unknown
64    /// condition type parses as `Condition::Unknown` and evaluates to
65    /// `NotApplicable`, which fails closed — see
66    /// [`crate::wac::conditions`].
67    #[serde(
68        rename = "acl:condition",
69        default,
70        skip_serializing_if = "Option::is_none"
71    )]
72    pub condition: Option<Vec<Condition>>,
73}
74
75/// One or more JSON-LD `@id` references, modelling both singular and array ACL values.
76#[derive(Debug, Clone, Deserialize, Serialize)]
77#[serde(untagged)]
78pub enum IdOrIds {
79    /// A single `@id` reference.
80    Single(IdRef),
81    /// An array of `@id` references.
82    Multiple(Vec<IdRef>),
83}
84
85/// A JSON-LD node reference carrying a single `@id` IRI.
86#[derive(Debug, Clone, Deserialize, Serialize)]
87pub struct IdRef {
88    /// The IRI value.
89    #[serde(rename = "@id")]
90    pub id: String,
91}
92
93pub(crate) fn get_ids(val: &Option<IdOrIds>) -> Vec<&str> {
94    match val {
95        None => Vec::new(),
96        Some(IdOrIds::Single(r)) => vec![r.id.as_str()],
97        Some(IdOrIds::Multiple(refs)) => refs.iter().map(|r| r.id.as_str()).collect(),
98    }
99}
100
101pub(crate) fn ids_of(items: Vec<String>) -> IdOrIds {
102    if items.len() == 1 {
103        IdOrIds::Single(IdRef {
104            id: items.into_iter().next().unwrap(),
105        })
106    } else {
107        IdOrIds::Multiple(items.into_iter().map(|id| IdRef { id }).collect())
108    }
109}