nio_client/
lib.rs

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);
23const PERSONAL: &str = "personal";
24const ROOT: &str = "root";
25const TOKEN: &str = "token";
26const SERVICEACCOUNT_NS: &str = "serviceaccount";
27impl Namespace {
28    pub fn personal() -> Namespace {
29        Namespace(PERSONAL.into())
30    }
31    pub fn token() -> Namespace {
32        Namespace(TOKEN.into())
33    }
34    pub fn root() -> Namespace {
35        Namespace(ROOT.into())
36    }
37    pub fn serviceaccount() -> Namespace {
38        Namespace(SERVICEACCOUNT_NS.into())
39    }
40}
41
42#[derive(Clone, Debug)]
43pub struct Permission(pub &'static str);
44
45#[derive(Clone, Debug)]
46pub struct Rel(pub String);
47impl Rel {
48    pub const TRIPLE_DOT: &'static str = "...";
49    pub const IS: &'static str = "is";
50    pub const PARENT: &'static str = "parent";
51    pub const IAM_GET: &'static str = "iam.get";
52    pub const IAM_UPDATE: &'static str = "iam.update";
53    pub const IAM_DELETE: &'static str = "iam.delete";
54    pub const SERVICEACCOUNT_GET: &'static str = "serviceaccount.get";
55    pub const SERVICEACCOUNT_CREATE: &'static str = "serviceaccount.create";
56    pub const SERVICEACCOUNT_KEY_GET: &'static str = "serviceaccount.key.get";
57    pub const SERVICEACCOUNT_KEY_CREATE: &'static str = "serviceaccount.key.create";
58    pub const SERVICEACCOUNT_CREATE_TOKEN: &'static str = "serviceaccount.createToken";
59    pub const USER_CREATE: &'static str = "user.create";
60
61    pub fn triple_dot() -> Rel {
62        Rel(Self::TRIPLE_DOT.into())
63    }
64    pub fn is() -> Rel {
65        Rel(Self::IS.into())
66    }
67    pub fn parent() -> Rel {
68        Rel(Self::PARENT.into())
69    }
70    pub fn iam_get() -> Rel {
71        Rel(Self::IAM_GET.into())
72    }
73    pub fn iam_update() -> Rel {
74        Rel(Self::IAM_UPDATE.into())
75    }
76    pub fn iam_delete() -> Rel {
77        Rel(Self::IAM_DELETE.into())
78    }
79    pub fn serviceaccount_get() -> Rel {
80        Rel(Self::SERVICEACCOUNT_GET.into())
81    }
82    pub fn serviceaccount_create() -> Rel {
83        Rel(Self::SERVICEACCOUNT_CREATE.into())
84    }
85    pub fn serviceaccount_key_get() -> Rel {
86        Rel(Self::SERVICEACCOUNT_KEY_GET.into())
87    }
88    pub fn serviceaccount_key_create() -> Rel {
89        Rel(Self::SERVICEACCOUNT_KEY_CREATE.into())
90    }
91    pub fn serviceaccount_create_token() -> Rel {
92        Rel(Self::SERVICEACCOUNT_CREATE_TOKEN.into())
93    }
94    pub fn user_upsert() -> Rel {
95        Rel(Self::USER_CREATE.into())
96    }
97}
98
99impl From<Permission> for Rel {
100    fn from(value: Permission) -> Self {
101        Rel(value.0.to_string())
102    }
103}
104
105#[derive(Clone, Debug)]
106pub struct UserId(pub String);
107
108#[derive(Clone, Debug)]
109pub struct Timestamp(pub String);
110
111impl Timestamp {
112    pub fn empty() -> Self {
113        Timestamp("1:0000000000000".into())
114    }
115}
116
117impl FromStr for UserId {
118    type Err = ParseError;
119
120    fn from_str(s: &str) -> Result<Self, Self::Err> {
121        Ok(UserId(s.into()))
122    }
123}
124
125impl TryFrom<String> for UserId {
126    type Error = ParseError;
127    fn try_from(value: String) -> Result<Self, Self::Error> {
128        UserId::from_str(&value)
129    }
130}
131
132#[derive(Clone, Debug)]
133pub struct Obj(pub String);
134const ROOT_OBJ: &str = "root";
135const UNSPECIFIED_OBJ: &str = "...";
136impl Obj {
137    pub fn unspecified() -> Obj {
138        Obj(UNSPECIFIED_OBJ.into())
139    }
140    pub fn root() -> Obj {
141        Obj(ROOT_OBJ.into())
142    }
143}
144
145impl FromStr for Obj {
146    type Err = ParseError;
147
148    fn from_str(s: &str) -> Result<Self, Self::Err> {
149        Ok(Obj(s.into()))
150    }
151}
152
153impl TryFrom<String> for Obj {
154    type Error = ParseError;
155    fn try_from(value: String) -> Result<Self, Self::Error> {
156        Obj::from_str(&value)
157    }
158}
159
160#[derive(Clone, Debug)]
161pub struct Tuple {
162    pub ns: Namespace,
163    pub obj: Obj,
164    pub rel: Rel,
165    pub sbj: User,
166}
167
168impl Display for Tuple {
169    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
170        match self {
171            Tuple {
172                ref ns,
173                ref obj,
174                ref rel,
175                sbj: User::UserId(ref s),
176            } => write!(f, "Tuple({}:{}#{}@{})", ns.0, obj.0, rel.0, s),
177            Tuple {
178                ref ns,
179                ref obj,
180                ref rel,
181                sbj:
182                    User::UserSet {
183                        ns: ref ns2,
184                        obj: ref obj2,
185                        rel: ref rel2,
186                    },
187            } => write!(
188                f,
189                "Tuple({}:{}#{}@{}:{}#{})",
190                ns.0, obj.0, rel.0, ns2.0, obj2.0, rel2.0
191            ),
192        }
193    }
194}
195
196#[derive(Clone, Debug)]
197pub enum Condition {
198    Expires(DateTime<Utc>),
199}
200
201#[derive(Clone, Debug)]
202pub enum User {
203    UserId(String),
204    UserSet { ns: Namespace, obj: Obj, rel: Rel },
205}
206
207mod pb {
208    tonic::include_proto!("am");
209}
210
211#[derive(Clone, Debug)]
212pub struct CheckClient {
213    pub(crate) client: pb::check_service_client::CheckServiceClient<Channel>,
214}
215
216impl CheckClient {
217    pub async fn get_all(&mut self, ns: &Namespace, obj: &Obj) -> Result<Vec<Tuple>, ReadError> {
218        let response = self
219            .client
220            .read(pb::ReadRequest {
221                tuple_sets: vec![TupleSet {
222                    ns: ns.0.clone(),
223                    spec: Some(pb::tuple_set::Spec::ObjectSpec(ObjectSpec {
224                        obj: obj.0.clone(),
225                        rel: None,
226                    })),
227                }],
228                ts: None,
229            })
230            .await?;
231        let response = response.into_inner();
232        Ok(response
233            .tuples
234            .into_iter()
235            .map(|tup| Tuple {
236                ns: Namespace(tup.ns),
237                obj: Obj(tup.obj),
238                rel: Rel(tup.rel),
239                sbj: match tup.user {
240                    None => todo!(),
241                    Some(user) => match user {
242                        pb::tuple::User::UserId(userid) => User::UserId(userid),
243                        pb::tuple::User::UserSet(pb::UserSet { ns, obj, rel }) => User::UserSet {
244                            ns: Namespace(ns),
245                            obj: Obj(obj),
246                            rel: Rel(rel),
247                        },
248                    },
249                },
250            })
251            .collect())
252    }
253}
254impl CheckClient {
255    pub async fn create(uri: Uri) -> Result<Self, ConnectError> {
256        match Channel::builder(uri).connect().await {
257            Ok(channel) => Ok(CheckClient {
258                client: pb::check_service_client::CheckServiceClient::new(channel),
259            }),
260            Err(err) => Err(ConnectError(err)),
261        }
262    }
263    pub async fn add_one(&mut self, tuple: Tuple) -> Result<String, AddError> {
264        let add_tuples = vec![pb::Tuple {
265            ns: tuple.ns.0.to_string(),
266            obj: tuple.obj.0,
267            rel: tuple.rel.0.to_string(),
268            user: match tuple.sbj {
269                User::UserId(user_id) => Some(pb::tuple::User::UserId(user_id)),
270                User::UserSet {
271                    ns: Namespace(ns),
272                    obj: Obj(obj),
273                    rel: Rel(rel),
274                } => Some(pb::tuple::User::UserSet(pb::UserSet {
275                    ns: ns.to_string(),
276                    obj,
277                    rel: rel.to_string(),
278                })),
279            },
280            condition: None,
281        }];
282        dbg!(&add_tuples);
283        self.client
284            .write(pb::WriteRequest {
285                add_tuples,
286                ..Default::default()
287            })
288            .await
289            .map(|r| r.into_inner().ts)
290            .map_err(Into::into)
291    }
292
293    pub async fn add_one_with_condition(
294        &mut self,
295        tuple: Tuple,
296        condition: Condition,
297    ) -> Result<String, AddError> {
298        let add_tuples = vec![pb::Tuple {
299            ns: tuple.ns.0.to_string(),
300            obj: tuple.obj.0,
301            rel: tuple.rel.0.to_string(),
302            user: match tuple.sbj {
303                User::UserId(user_id) => Some(pb::tuple::User::UserId(user_id)),
304                User::UserSet {
305                    ns: Namespace(ns),
306                    obj: Obj(obj),
307                    rel: Rel(rel),
308                } => Some(pb::tuple::User::UserSet(pb::UserSet {
309                    ns: ns.to_string(),
310                    obj,
311                    rel: rel.to_string(),
312                })),
313            },
314            condition: match condition {
315                Condition::Expires(exp) => Some(pb::tuple::Condition::Expires(exp.timestamp())),
316            },
317        }];
318        self.client
319            .write(pb::WriteRequest {
320                add_tuples,
321                ..Default::default()
322            })
323            .await
324            .map(|r| r.into_inner().ts)
325            .map_err(Into::into)
326    }
327
328    pub async fn add_many(&mut self, tuples: Vec<Tuple>) -> Result<String, AddError> {
329        let add_tuples = tuples
330            .into_iter()
331            .map(|tuple| pb::Tuple {
332                ns: tuple.ns.0,
333                obj: tuple.obj.0,
334                rel: tuple.rel.0,
335                user: match tuple.sbj {
336                    User::UserId(user_id) => Some(pb::tuple::User::UserId(user_id)),
337                    User::UserSet {
338                        ns: Namespace(ns),
339                        obj: Obj(obj),
340                        rel: Rel(rel),
341                    } => Some(pb::tuple::User::UserSet(pb::UserSet { ns, obj, rel })),
342                },
343                condition: None,
344            })
345            .collect();
346        self.client
347            .write(pb::WriteRequest {
348                add_tuples,
349                ..Default::default()
350            })
351            .await
352            .map(|r| r.into_inner().ts)
353            .map_err(Into::into)
354    }
355
356    pub async fn delete_one(&mut self, tuple: Tuple) -> Result<String, AddError> {
357        let del_tuples = vec![pb::Tuple {
358            ns: tuple.ns.0,
359            obj: tuple.obj.0,
360            rel: tuple.rel.0.to_string(),
361            user: match tuple.sbj {
362                User::UserId(user_id) => Some(pb::tuple::User::UserId(user_id)),
363                User::UserSet {
364                    ns: Namespace(ns),
365                    obj: Obj(obj),
366                    rel: Rel(rel),
367                } => Some(pb::tuple::User::UserSet(pb::UserSet {
368                    ns: ns.to_string(),
369                    obj,
370                    rel: rel.to_string(),
371                })),
372            },
373            condition: None,
374        }];
375        self.client
376            .write(pb::WriteRequest {
377                del_tuples,
378                ..Default::default()
379            })
380            .await
381            .map(|r| r.into_inner().ts)
382            .map_err(Into::into)
383    }
384}
385
386impl CheckClient {
387    pub async fn check(
388        &mut self,
389        Namespace(ns): Namespace,
390        Obj(obj): Obj,
391        Permission(permission): Permission,
392        UserId(user_id): UserId,
393        timestamp: Option<Timestamp>,
394    ) -> Result<CheckResult, CallError> {
395        let r = pb::CheckRequest {
396            ns: ns.to_string(),
397            obj,
398            rel: permission.to_string(),
399            user_id,
400            ts: timestamp.unwrap_or(Timestamp::empty()).0,
401        };
402        match self.client.check(r).await.map(|r| r.into_inner()) {
403            Ok(pb::CheckResponse {
404                principal: Some(pb::Principal { id }),
405                ok,
406            }) if ok => Ok(CheckResult::Ok(id.into())),
407            Ok(pb::CheckResponse {
408                principal: Some(pb::Principal { id }),
409                ok,
410            }) if !ok => Ok(CheckResult::Forbidden(id.into())),
411            Ok(pb::CheckResponse {
412                principal: None, ..
413            }) => Ok(CheckResult::UnknownPutativeUser),
414            Ok(pb::CheckResponse { .. }) => Err(CallError::UnexpectedResponseFormat),
415            Err(status) => Err(status.into()),
416        }
417    }
418
419    pub async fn list(
420        &mut self,
421        Namespace(ns): Namespace,
422        Rel(rel): Rel,
423        UserId(user_id): UserId,
424        timestamp: Option<Timestamp>,
425    ) -> Result<Vec<String>, CallError> {
426        let r = pb::ListRequest {
427            ns,
428            rel: rel.to_string(),
429            user_id,
430            ts: timestamp.unwrap_or(Timestamp::empty()).0,
431        };
432        match self.client.list(r).await.map(|r| r.into_inner()) {
433            Ok(response) => Ok(response.objs),
434            Err(e) => Err(e.into()),
435        }
436    }
437}