1#![allow(unused_variables)]
2#![allow(unused_imports)]
3use std::fmt::{Display, Formatter};
4use std::str::FromStr;
5
6use crate::auth::{CallError, CheckResult};
7use crate::error::ReadError;
8use crate::pb::tuple_set::ObjectSpec;
9use crate::pb::TupleSet;
10use chrono::{DateTime, Utc};
11pub use error::ConnectError;
12use error::{AddError, ParseError};
13use http::Uri;
14use tonic::transport::Channel;
15
16pub mod auth;
17#[cfg(feature = "axum")]
18pub mod axum;
19mod error;
20
21#[derive(Clone, Debug)]
22pub struct Namespace(pub String);
23
24#[derive(Clone, Debug)]
25pub struct Permission(pub &'static str);
26
27#[derive(Clone, Debug)]
28pub struct Rel(pub String);
29
30impl From<Permission> for Rel {
31 fn from(value: Permission) -> Self {
32 Rel(value.0.to_string())
33 }
34}
35
36#[derive(Clone, Debug)]
37pub struct UserId(pub String);
38
39#[derive(Clone, Debug)]
40pub struct Timestamp(pub String);
41
42impl Timestamp {
43 pub fn empty() -> Self {
44 Timestamp("1:0000000000000".into())
45 }
46}
47
48impl FromStr for UserId {
49 type Err = ParseError;
50
51 fn from_str(s: &str) -> Result<Self, Self::Err> {
52 Ok(UserId(s.into()))
53 }
54}
55
56impl TryFrom<String> for UserId {
57 type Error = ParseError;
58 fn try_from(value: String) -> Result<Self, Self::Error> {
59 UserId::from_str(&value)
60 }
61}
62
63#[derive(Clone, Debug)]
64pub struct Obj(pub String);
65
66impl FromStr for Obj {
67 type Err = ParseError;
68
69 fn from_str(s: &str) -> Result<Self, Self::Err> {
70 Ok(Obj(s.into()))
71 }
72}
73
74impl TryFrom<String> for Obj {
75 type Error = ParseError;
76 fn try_from(value: String) -> Result<Self, Self::Error> {
77 Obj::from_str(&value)
78 }
79}
80
81#[derive(Clone, Debug)]
82pub struct Tuple {
83 pub ns: Namespace,
84 pub obj: Obj,
85 pub rel: Rel,
86 pub sbj: User,
87}
88
89impl Display for Tuple {
90 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
91 match self {
92 Tuple {
93 ref ns,
94 ref obj,
95 ref rel,
96 sbj: User::UserId(ref s),
97 } => write!(f, "Tuple({}:{}#{}@{})", ns.0, obj.0, rel.0, s),
98 Tuple {
99 ref ns,
100 ref obj,
101 ref rel,
102 sbj:
103 User::UserSet {
104 ns: ref ns2,
105 obj: ref obj2,
106 rel: ref rel2,
107 },
108 } => write!(
109 f,
110 "Tuple({}:{}#{}@{}:{}#{})",
111 ns.0, obj.0, rel.0, ns2.0, obj2.0, rel2.0
112 ),
113 }
114 }
115}
116
117#[derive(Clone, Debug)]
118pub enum Condition {
119 Expires(DateTime<Utc>),
120}
121
122#[derive(Clone, Debug)]
123pub enum User {
124 UserId(String),
125 UserSet { ns: Namespace, obj: Obj, rel: Rel },
126}
127
128mod pb {
129 tonic::include_proto!("am");
130}
131
132#[derive(Clone, Debug)]
133pub struct CheckClient {
134 pub(crate) client: pb::check_service_client::CheckServiceClient<Channel>,
135}
136
137impl CheckClient {
138 pub async fn get_all(&mut self, ns: &Namespace, obj: &Obj) -> Result<Vec<Tuple>, ReadError> {
155 let response = self
156 .client
157 .read(pb::ReadRequest {
158 tuple_sets: vec![TupleSet {
159 ns: ns.0.clone(),
160 spec: Some(pb::tuple_set::Spec::ObjectSpec(ObjectSpec {
161 obj: obj.0.clone(),
162 rel: None,
163 })),
164 }],
165 ts: None,
166 })
167 .await?;
168 let response = response.into_inner();
169 Ok(response
170 .tuples
171 .into_iter()
172 .map(|tup| Tuple {
173 ns: Namespace(tup.ns),
174 obj: Obj(tup.obj),
175 rel: Rel(tup.rel),
176 sbj: match tup.user {
177 None => todo!(),
178 Some(user) => match user {
179 pb::tuple::User::UserId(userid) => User::UserId(userid),
180 pb::tuple::User::UserSet(pb::UserSet { ns, obj, rel }) => User::UserSet {
181 ns: Namespace(ns),
182 obj: Obj(obj),
183 rel: Rel(rel),
184 },
185 },
186 },
187 })
188 .collect())
189 }
190 }
192impl CheckClient {
193 pub async fn create(uri: Uri) -> Result<Self, ConnectError> {
194 match Channel::builder(uri).connect().await {
195 Ok(channel) => Ok(CheckClient {
196 client: pb::check_service_client::CheckServiceClient::new(channel),
197 }),
198 Err(err) => Err(ConnectError(err)),
199 }
200 }
201 pub async fn add_one(&mut self, tuple: Tuple) -> Result<String, AddError> {
208 let add_tuples = vec![pb::Tuple {
209 ns: tuple.ns.0.to_string(),
210 obj: tuple.obj.0,
211 rel: tuple.rel.0.to_string(),
212 user: match tuple.sbj {
213 User::UserId(user_id) => Some(pb::tuple::User::UserId(user_id)),
214 User::UserSet {
215 ns: Namespace(ns),
216 obj: Obj(obj),
217 rel: Rel(rel),
218 } => Some(pb::tuple::User::UserSet(pb::UserSet {
219 ns: ns.to_string(),
220 obj,
221 rel: rel.to_string(),
222 })),
223 },
224 condition: None,
225 }];
226 dbg!(&add_tuples);
227 self.client
228 .write(pb::WriteRequest {
229 add_tuples,
230 ..Default::default()
231 })
232 .await
233 .map(|r| r.into_inner().ts)
234 .map_err(Into::into)
235 }
236
237 pub async fn add_one_with_condition(
238 &mut self,
239 tuple: Tuple,
240 condition: Condition,
241 ) -> Result<String, AddError> {
242 let add_tuples = vec![pb::Tuple {
243 ns: tuple.ns.0.to_string(),
244 obj: tuple.obj.0,
245 rel: tuple.rel.0.to_string(),
246 user: match tuple.sbj {
247 User::UserId(user_id) => Some(pb::tuple::User::UserId(user_id)),
248 User::UserSet {
249 ns: Namespace(ns),
250 obj: Obj(obj),
251 rel: Rel(rel),
252 } => Some(pb::tuple::User::UserSet(pb::UserSet {
253 ns: ns.to_string(),
254 obj,
255 rel: rel.to_string(),
256 })),
257 },
258 condition: match condition {
259 Condition::Expires(exp) => Some(pb::tuple::Condition::Expires(exp.timestamp())),
260 },
261 }];
262 self.client
263 .write(pb::WriteRequest {
264 add_tuples,
265 ..Default::default()
266 })
267 .await
268 .map(|r| r.into_inner().ts)
269 .map_err(Into::into)
270 }
271
272 pub async fn add_many(&mut self, tuples: Vec<Tuple>) -> Result<String, AddError> {
273 let add_tuples = tuples
274 .into_iter()
275 .map(|tuple| pb::Tuple {
276 ns: tuple.ns.0,
277 obj: tuple.obj.0,
278 rel: tuple.rel.0,
279 user: match tuple.sbj {
280 User::UserId(user_id) => Some(pb::tuple::User::UserId(user_id)),
281 User::UserSet {
282 ns: Namespace(ns),
283 obj: Obj(obj),
284 rel: Rel(rel),
285 } => Some(pb::tuple::User::UserSet(pb::UserSet { ns, obj, rel })),
286 },
287 condition: None,
288 })
289 .collect();
290 self.client
291 .write(pb::WriteRequest {
292 add_tuples,
293 ..Default::default()
294 })
295 .await
296 .map(|r| r.into_inner().ts)
297 .map_err(Into::into)
298 }
299
300 pub async fn delete_one(&mut self, tuple: Tuple) -> Result<String, AddError> {
301 let del_tuples = vec![pb::Tuple {
302 ns: tuple.ns.0,
303 obj: tuple.obj.0,
304 rel: tuple.rel.0.to_string(),
305 user: match tuple.sbj {
306 User::UserId(user_id) => Some(pb::tuple::User::UserId(user_id)),
307 User::UserSet {
308 ns: Namespace(ns),
309 obj: Obj(obj),
310 rel: Rel(rel),
311 } => Some(pb::tuple::User::UserSet(pb::UserSet {
312 ns: ns.to_string(),
313 obj,
314 rel: rel.to_string(),
315 })),
316 },
317 condition: None,
318 }];
319 self.client
320 .write(pb::WriteRequest {
321 del_tuples,
322 ..Default::default()
323 })
324 .await
325 .map(|r| r.into_inner().ts)
326 .map_err(Into::into)
327 }
328}
329
330impl CheckClient {
344 pub async fn check(
345 &mut self,
346 Namespace(ns): Namespace,
347 Obj(obj): Obj,
348 Permission(permission): Permission,
349 UserId(user_id): UserId,
350 timestamp: Option<Timestamp>,
351 ) -> Result<CheckResult, CallError> {
352 let r = pb::CheckRequest {
353 ns: ns.to_string(),
354 obj,
355 rel: permission.to_string(),
356 user_id,
357 ts: timestamp.unwrap_or(Timestamp::empty()).0,
358 };
359 match self.client.check(r).await.map(|r| r.into_inner()) {
360 Ok(pb::CheckResponse {
361 principal: Some(pb::Principal { id }),
362 ok,
363 }) if ok => Ok(CheckResult::Ok(id.into())),
364 Ok(pb::CheckResponse {
365 principal: Some(pb::Principal { id }),
366 ok,
367 }) if !ok => Ok(CheckResult::Forbidden(id.into())),
368 Ok(pb::CheckResponse {
369 principal: None, ..
370 }) => Ok(CheckResult::UnknownPutativeUser),
371 Ok(pb::CheckResponse { .. }) => Err(CallError::UnexpectedResponseFormat),
372 Err(status) => Err(status.into()),
373 }
374 }
375
376 pub async fn list(
377 &mut self,
378 Namespace(ns): Namespace,
379 Rel(rel): Rel,
380 UserId(user_id): UserId,
381 timestamp: Option<Timestamp>,
382 ) -> Result<Vec<String>, CallError> {
383 let r = pb::ListRequest {
384 ns,
385 rel: rel.to_string(),
386 user_id,
387 ts: timestamp.unwrap_or(Timestamp::empty()).0,
388 };
389 match self.client.list(r).await.map(|r| r.into_inner()) {
390 Ok(response) => Ok(response.objs),
391 Err(e) => Err(e.into()),
392 }
393 }
394}