1use core::fmt;
6
7use crate::{agents::ForAgent, errors::AtomicResult, storelike::Query, urls, Resource, Storelike};
8
9#[derive(Debug)]
10pub enum Right {
11 Read,
14 Write,
17 Append,
20}
21
22impl fmt::Display for Right {
23 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
24 let str = match self {
25 Right::Read => urls::READ,
26 Right::Write => urls::WRITE,
27 Right::Append => urls::APPEND,
28 };
29 fmt.write_str(str)
30 }
31}
32
33pub fn add_children(store: &impl Storelike, resource: &mut Resource) -> AtomicResult<Resource> {
35 let results = store.query(&Query::new_prop_val(urls::PARENT, resource.get_subject()))?;
36 let mut children = results.subjects;
37 children.sort();
38 resource.set(urls::CHILDREN.into(), children.into(), store)?;
39 Ok(resource.to_owned())
40}
41
42pub fn check_write(
45 store: &impl Storelike,
46 resource: &Resource,
47 for_agent: &ForAgent,
48) -> AtomicResult<String> {
49 check_rights(store, resource, for_agent, Right::Write)
50}
51
52pub fn check_read(
56 store: &impl Storelike,
57 resource: &Resource,
58 for_agent: &ForAgent,
59) -> AtomicResult<String> {
60 check_rights(store, resource, for_agent, Right::Read)
61}
62
63#[tracing::instrument(skip(store), level = "debug")]
68pub fn check_append(
69 store: &impl Storelike,
70 resource: &Resource,
71 for_agent: &ForAgent,
72) -> AtomicResult<String> {
73 match resource.get_parent(store) {
74 Ok(parent) => {
75 if let Ok(msg) = check_rights(store, &parent, for_agent, Right::Append) {
76 Ok(msg)
77 } else {
78 check_rights(store, resource, for_agent, Right::Write)
79 }
80 }
81 Err(e) => {
82 if resource
83 .get_classes(store)?
84 .iter()
85 .map(|c| c.subject.clone())
86 .collect::<String>()
87 .contains(urls::DRIVE)
88 {
89 Ok(String::from("Drive without a parent can be created"))
90 } else {
91 Err(e)
92 }
93 }
94 }
95}
96
97#[tracing::instrument(skip(store, resource))]
101pub fn check_rights(
102 store: &impl Storelike,
103 resource: &Resource,
104 for_agent_enum: &ForAgent,
105 right: Right,
106) -> AtomicResult<String> {
107 if for_agent_enum == &ForAgent::Sudo {
108 return Ok("Sudo has root access, and can edit anything.".into());
109 }
110 let for_agent = for_agent_enum.to_string();
111 if resource.get_subject() == &for_agent {
112 return Ok("Agents can always edit themselves or their children.".into());
113 }
114 if let Ok(server_agent) = store.get_default_agent() {
115 if server_agent.subject == for_agent {
116 return Ok("Server agent has root access, and can edit anything.".into());
117 }
118 }
119
120 if let Ok(commit_subject) = resource.get(urls::SUBJECT) {
122 return match right {
123 Right::Read => {
124 let target = store.get_resource(&commit_subject.to_string())?;
126 check_rights(store, &target, for_agent_enum, right)
127 }
128 Right::Write => Err("Commits cannot be edited.".into()),
129 Right::Append => Err("Commits cannot have children, you cannot Append to them.".into()),
130 };
131 }
132
133 if let Ok(arr_val) = resource.get(&right.to_string()) {
135 for s in arr_val.to_subjects(None)? {
136 match s.as_str() {
137 urls::PUBLIC_AGENT => {
138 return Ok(format!(
139 "PublicAgent has been granted rights in {}",
140 resource.get_subject()
141 ))
142 }
143 agent => {
144 if agent == for_agent {
145 return Ok(format!(
146 "Right has been explicitly set in {}",
147 resource.get_subject()
148 ));
149 }
150 }
151 };
152 }
153 }
154
155 if let Ok(parent) = resource.get_parent(store) {
157 check_rights(store, &parent, for_agent_enum, right)
158 } else {
159 if for_agent_enum == &ForAgent::Public {
160 let action = match right {
162 Right::Read => "readable",
163 Right::Write => "editable",
164 Right::Append => "appendable",
165 };
166 return Err(crate::errors::AtomicError::unauthorized(format!(
167 "This resource is not publicly {}. Try signing in",
168 action,
169 )));
170 }
171 Err(crate::errors::AtomicError::unauthorized(format!(
173 "No {} right has been found for {} in this resource or its parents",
174 right, for_agent
175 )))
176 }
177}
178
179#[cfg(test)]
180mod test {
181 use crate::{datatype::DataType, Storelike, Value};
183
184 #[test]
189 fn authorization() {
190 let store = crate::Store::init().unwrap();
191 store.populate().unwrap();
192 let subject = "https://localhost/new_thing";
194 let mut commitbuilder_1 = crate::commit::CommitBuilder::new(subject.into());
195 let property = crate::urls::DESCRIPTION;
196 let value = Value::new("Some value", &DataType::Markdown).unwrap();
197 commitbuilder_1.set(property.into(), value);
198 }
209
210 #[test]
211 fn display_right() {
212 let read = super::Right::Read;
213 assert_eq!(read.to_string(), super::urls::READ);
214 let write = super::Right::Write;
215 assert_eq!(write.to_string(), super::urls::WRITE);
216 }
217}